Mastro 👨‍🍳 Docs

Search results

Blog Community GitHub   Stoat Chat   Bluesky

Third-party packages

For complex functionality that would take you a long time to write yourself, it can be useful to install packages from NPM or JSR. These are package repositories where people share code for you to use. However, take a bit of time to evaluate a package before adding it as a dependency to your project. Code quality, bundle size, security practices, and long-term maintenance outlook varies a lot between different third-party packages.

Since it’s run in very different environments, client-side and server-side JavaScript is separated on purpose in Mastro. The former needs to be downloaded to the browser of your users, the latter runs in a runtime like Deno or Node.js, which you control. Let’s look at how you add packages for each of them.

On the server

You might want to use the markdown-it package from NPM, instead of the @mastrojs/markdown package to generate HTML from your markdown files on the server.

To install the package, use your package manager as follows:

deno add npm:markdown-it
Copied!

which will add this line to your deno.json file:

deno.json
{
  "name": "...",
  "imports": {
    "markdown-it": "npm:markdown-it@14.1.0"
  }
}
Copied!
pnpm add markdown-it
Copied!

which will not only change the package.json file, but also run pnpm install.

package.json
{
  "name": "...",
  "dependencies": {
    "markdown-it": "markdown-it@14.1.0"
  }
}
Copied!
bun add markdown-it
Copied!

which will not only change the package.json file, but also run bun install.

package.json
{
  "name": "...",
  "dependencies": {
    "markdown-it": "markdown-it@14.1.0"
  }
}
Copied!

When generating a static site with the Mastro VS Code for the Web extension, Mastro will look for an import_map.json file to resolve the markdown-it specifier. Using esm.sh:

import_map.json
{
  "imports": {
    "markdown-it": "https://esm.sh/markdown-it@14.1.0"
  }
}
Copied!

Then you’ll be able to import the package in any server-side JavaScript file:

import markdownIt from "markdown-it";
Copied!

On the client

It’s important to remember that the above file (deno.json or package.json) will not be loaded into the browser – and unless you use a bundler, neither will any packages it contains. This is a good, because packages intended to be run on the server are often very big and would slow down your website if all your website visitors had to download them.

Bundling

We’ll look at setting up a bundler with Mastro later. But to get started, it’s easier to use pre-bundled versions – either by using a CDN or self-hosted.

CDN

If the library you want to add is pre-bundling and published to a CDN (e.g. jsDelivr or UNPKG), use that. If they’re only advertising an NPM (or JSR) package, you can use the esm.sh CDN. To enable the bundler that’s built into esm.sh, add ?bundle at the end of the URL:

<!doctype html>
<html>
  <head>
    <script type="importmap">
    {
      "imports": {
        "markdown-it": "https://esm.sh/markdown-it@14.1.0?bundle"
      }
    }
    </script>
Copied!

Self-hosted

If you want to self-host your client-side dependencies instead of relying on a third-party CDN, you can download pre-bundled versions from a CDN or from NPM. Often, you can find bundled files in a dist/ folder or similar in the Code tab of the package on NPM. For the markdown-it example, this would be the dist/markdown-it.min.js file on this page.

After downloading the file, add it somewhere in your Mastro project’s routes/ folder, e.g. routes/vendor/markdown-it/markdown-it.min.js. You can choose the exact folder names and structure, but vendor is a common name when “vendoring” – i.e. copying another project’s code into your project. Then you can load it like:

<!doctype html>
<html>
  <head>
    <script type="importmap">
    {
      "imports": {
        "markdown-it": "/vendor/markdown-it/markdown-it.min.js"
      }
    }
    </script>
Copied!

Import maps

In Deno and the browser you could also use the URL of any ESM module as part of an import statement. However, if you’ll use the library in more than one file, it’s best to centralize it in an import map – like demonstrated in the CDN and self-hosted examples above. Then, there’s only one place to change the version number, when the time comes to update it.

You’ll see a full example of a client-side importmap in the next chapter.