Mastro 👨‍🍳 Docs

Search results

Blog Community GitHub   Discord   Bluesky

Installation and setup

There are various way to run Mastro.

Install Mastro (CLI) VS Code for Web (online)

SSG and SSR

Mastro supports both static site generation (SSG), and running a server with on-demand server-side rendering (SSR). See the guide for advantages and disadvantages of each mode.

The good news is that both static site generation and running a server work the same with Mastro. You can even run a server and still pregenerate some pages.

Start a server

To start a local development server:

This actually runs the same server as you would probably run in production for on-demand rendering (with the exception of the --watch flag). Check out the deno.json/package.json, which is just running the server.ts file that was in the template repo. This server.ts is the entrypoint to your application, and where you call the mastro.fetch handler – yes, Mastro is actually just a library.

Generate a static site

This will create a generated folder by passing synthetic Request objects to your route handlers.

To see the generate CLI options, append --help. For example: deno task generate --help

Configuring a base path

If you’re hosting your website on a sub-directory (e.g. https://mydomain.org/sub-directory), you need to prefix all absolute links that start with a slash with your base-path (/sub-directory in this case). This includes links to assets like CSS files, as well as internals links to other pages on your website.

GitHub Pages

This is very common when using GitHub Pages without a custom domain. It’s needed when you’re hosting on https://my-name.github.io/my-repo (instead of https://my-name.github.io). For GitHub Pages, Mastro has the ghPagesBasePath helper function built in (which will return e.g. /my-repo when run on GitHub Actions). You can use it like:

components/Layout.ts
import { ghPagesBasePath, html } from "@mastrojs/mastro";
// we export `basePath` for use in other modules
export const basePath = ghPagesBasePath();
export const GET = () =>
  html`
    <html>
      <head>
        <link href=${basePath + "/styles.css"}>
  `;
Copied!

Other hosts

If you’re not on GitHub Pages, but still hosting in a sub-directory, you can change the generate command in deno.json to:

BASEPATH='/my-path' deno task generate
Copied!

This sets an environment variable (if you’re building on Windows without WSL, the syntax will be slightly different). You can read it out like:

components/Layout.ts
// we export `basePath` for use in other modules
export const basePath = process.env.BASEPATH || "";
export const GET = () =>
  html`
    <html>
      <head>
        <link href=${basePath + "/styles.css"}>
  `;
Copied!

TypeScript

While you can also just use JavaScript, Mastro supports TypeScript out of the box. Since Deno, Node.js and Bun all natively support type-stripping, server TypeScript files are directly read by the respective runtime (no transpilation).

However, browsers are not there yet. Therefore, files in the routes/ folder that end with *.client.ts are transpiled to *.client.js on the fly using ts-blank-space – both when they are served via the server, and when a static site is generated. This also rewrites imports from .ts to .js, e.g. import foo from "./foo.ts" is transformed to import foo from "./foo.js". (To see the gory details, look for the tsToJs function in Mastro’s staticFiles.ts.)

Using ts-blank-space, which simply puts spaces and newlines where the types would have been, has the nice property of preserving the correct line numbers in error messages and stack traces. That’s why we added it to Mastro, even though we’re otherwise no-bundler, and wouldn’t add more disruptive transforms like JSX.

By itself, neither starting the server nor loading a .client.ts file will perform any type-checking. To check your project for type errors, run:

You porbably want to make sure this is executed as part of your deployment pipeline, for example by prepending deno check && to your generate task in deno.json (or pnpm run check && to package.json respectively).

Testing

To add tests, refer to the documentation of your platform’s built-in test runner:

Middleware

While Mastro itself doesn’t have the concept of a middleware, you can either:

server.ts
import mastro from "@mastrojs/mastro/server";

Deno.serve(async (req) => {
  // modify request here before it hits your Mastro routes
  const res = await mastro.fetch(req);
  // modify response returned by your Mastro routes
  return res;
});
Copied!

If there is demand, we could introduce a @mastrojs/middleware package that formalizes this concept somewhat.