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 configurationroot.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:
// @/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:
// @/routes/index.tsx
export default function Page() {
return <custom-heading>Hello, world!</custom-heading>;
}
Root.js adds the script to the rendered page:
<!-- 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.
// @/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.
// @/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.