Skip to main content

Project structure

The following is the typical file structure for a Root.js project. You can also explore one of our many examples on GitHub.

routes/

Routes are defined using .tsx files within the routes/ folder and will automatically render the default export as a TSX server-rendered component. Under the hood, the TSX is rendered using Preact's renderToString() function.

Below are a few examples of routes files and the final serving path:

Route Matching URL(s)
@/routes/index.tsx /
@/routes/about.tsx /about
@/routes/blog/index.tsx /blog
@/routes/blog/[slug].tsx
  • /blog/foo
  • /blog/bar

For more info, check out the Routes guide.

elements/

One of the key magical features that Root.js provides is the ability scan the rendered HTML page for custom elements and automatically inject inject any matching .ts/.tsx file found in the elements/ folder.

Example:

Create a file that defines your custom element:

tsx
// @/elements/custom-heading/custom-heading.ts

declare module 'preact' {
  namespace JSX {
    interface IntrinsicElements {
      'custom-heading': CustomHeadingProps;
    }
  }
}

interface CustomHeadingProps {...}

class CustomHeading {...}

window.customElements.define('custom-heading', CustomHeading);

Use that custom element within your route:

tsx
// @/routes/index.tsx

export default function Page() {
  return (
    <custom-heading>Hello World!</custom-heading>
  );
}

Within your rendered HTML you should see that the dependency is automatically added to the page:

html
<!-- rendered html -->
<!doctype html>
<html>
  <head>
    <script type="module" src="/elements/custom-heading/custom-heading.ts"></script>
  </head>
  <body>
    <custom-heading>Hello World!</custom-heading>
  </body>
</html>

translations/

Translations are stored as JSON files, where each locale is in its own file and stores a map of source string to translated string.

Example:

json
// @/translations/es.json

{
  "Hello World!": "¡Hola Mundo!",
  "Hello {name}!": "¡Hola {name}!"
}

Pages can use the useTranslations() hook which returns a function to translate strings for the current locale. The translations are provided via a Preact context at the root of the rendering tree.

tsx
// @/routes/index.tsx

import {useTranslations} from '@blinkk/root';

export default function Page() {
  const t = useTranslations();
  return (
    <h1>{t('Hello {name}!', {name: 'Alice'})}</h1>
  );
}

bundles/

The "bundles" directory is used for bundling client-side code outside of custom elements. This may be useful for loading 3rd-party libraries and other common global utilities that aren't necessarily encapsulated within a custom element.

Since files within "bundles" are compiled together with the files in the "elements" directory, the rendered output will smartly generate a module graph and intelligently chunk shared dependencies into separate files, minimizing the overall build output.

Example:

tsx
// @/routes/index.tsx

import {Script} from '@blinkk/root';

export default function Page() {
  return (
    <Script type="module" src="/bundles/main.ts" />
  );
}

public/

Use the public/ directory to serve static files. Useful for things like robots.txt and site verification files.

1
2
3
4
5
6
7
8
9
10
11
12
Breakpoint: