Skip to main content
Get started

Project structure

A typical Root.js project looks like the tree below. Only root.config.ts and routes/ are required. You can also browse the examples on GitHub.

my-site/
├── bundles/        # Client-side entry points
├── collections/    # CMS content models (*.schema.ts)
├── components/     # Shared TSX components
├── elements/       # Custom elements, loaded automatically
├── public/         # Static files, served as-is
├── routes/         # Pages, mapped to URLs by file path
├── translations/   # Translated strings ({locale}.json)
├── root-cms.d.ts   # Types generated from your schemas
└── root.config.ts  # Project configuration

root.config.ts

The project's configuration, loaded by every root command. See Configuration.

routes/

Each .tsx file in routes/ is a page, and its path sets the URL, e.g. routes/about.tsx serves /about. The default export is rendered to HTML on the server. See Routes.

elements/

Root.js scans each rendered page for custom elements, and automatically adds the matching file from elements/ to the page. Pages only load the JavaScript for the elements they use.

Define a custom element:

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

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

interface CustomHeadingProps {...}

class CustomHeading extends HTMLElement {...}

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

Use it in a route, with no import needed:

tsx
// @/routes/index.tsx

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

Root.js adds the script to the rendered 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>

See Interactive islands for more.

bundles/

Use bundles/ for client-side code that isn't tied to a custom element, like analytics or third-party libraries. Bundles are built together with elements/, so shared dependencies are split into common chunks.

tsx
// @/routes/index.tsx

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

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

collections/

CMS content models. Each .schema.ts file defines a collection, and root-cms generate-types writes matching TypeScript types to root-cms.d.ts. See Schemas.

translations/

Translated strings, one JSON file per locale, mapping each source string to its translation. See Localization.

json
// @/translations/es.json

{
  "Hello, world!": "¡Hola, mundo!",
  "Hello, {name}!": "¡Hola, {name}!"
}

public/

Static files served as-is from the site root, like robots.txt, favicons and site verification files.

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