Installation and setup
There are various way to run Mastro. If you prefer the command-line over running Mastro in the browser as a VSâCode extension, make sure one of the following JavaScript runtimes is installed. If youâre unsure which runtime to pick, we recommend Deno.
Running one of the following commands downloads the corresponding template repository into a newly created folder.
Deno
deno run -A npm:@mastrojs/create-mastro@0.0.8
Or use the template repo.
Node.js
Mastro supports Node.js >= 24. pnpm is recommended, although npm and yarn also work.
pnpm create @mastrojs/mastro@0.0.8
Or use the template repo.
Bun
bun create @mastrojs/mastro@0.0.8
Or use the template repo.
SSG, SSR and deploying
Mastro supports both static site generation (SSG) and running a server with on-demand server-side rendering (SSR). See the guide for more info.
To start your local development server, run:
- Deno:
deno task start - Node.js:
pnpm run start - Bun:
bun run start
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 basically just a library.
To generate a static site, run:
- Deno:
deno task generate - Node.js:
pnpm run generatet - Bun:
bun run generatet
This will create a generated folder by passing synthetic Request objects to your route handlers.
To publish your website, see deploy to production in the guide.
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 executed by the respective runtime.
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:
- Deno:
deno check - Node.js:
pnpm run check - Bun:
bun run check
You may 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 easily run some code on each request that hits your on-demand rendering server by modifying the server.ts file. For example in Deno:
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;
});
If there is demand, we could introduce a @mastrojs/middleware package that formalizes this concept somewhat.
Next steps
- Deploy your static site to production
- Deploy your server to production
- Add extensions
- Bundle client-side JavaScript, CSS or transform images
Do you have a question, need help, or would like to talk about future plans? Please start a GitHub discussion.
Something not working as expected? We consider that a bug. Open a GitHub issue.