Krate Docs

Getting Started

This guide walks you through installing the Krate CLI, scaffolding a project, and building your first page.

Prerequisites

  • Node.js 20+ — for the npm CLI wrapper and running krate.config.ts
  • Go 1.26+ — only needed when building the compiler from source

The published CLI ships pre-built binaries for macOS, Linux and Windows, so you do not need Go to build a site.

Install the CLI

npm install -g @krate/core
# or
pnpm add -g @krate/core

Create a project

npx create-krate-app@latest my-app

create-krate-app scaffolds a complete project — including krate.config.ts, tsconfig.json, package.json, a landing page, a layout, and a 404 page. With no directory it prompts for a name.

Add a page:

// src/pages/index.tsx
export default function Home() {
  return <h1>Hello, World!</h1>;
}

Build and run

krate dev       # dev server with hot reload on http://localhost:3000
krate build     # production build → dist/
krate serve     # preview the production build

Your first interactive page

Krate's reactivity is built on signals. A signal is a getter/setter pair; reading a signal inside an effect subscribes that effect to it.

import { createSignal } from '@krate/runtime';

export default function Counter() {
  const [count, setCount] = createSignal(0);
  return (
    <div>
      <span>{count()}</span>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  );
}
  • count() reads the current value.
  • setCount(c => c + 1) writes a new value and notifies subscribers.
  • At build time the renderer extracts the initial value and emits static HTML.
  • At runtime the hydration bundle wires the button to update the <span>.

Data fetching

Krate provides data through component tiers rather than page-level data functions:

// @server — evaluated at build time, HTML baked in
export default function ServerTime() {
  return <time>{new Date().toUTCString()}</time>;
}
// @runtime — evaluated per request, streamed via Suspense
export default function PriceTag({ price }) {
  return <span>{price}</span>;
}

Dynamic route pages receive their params as props via generateStaticParams. See Data Fetching for the full model.

Layouts

A _layout.tsx file wraps every page in its directory (and subdirectories):

// src/pages/_layout.tsx
export default function Layout({ children }) {
  return (
    <div>
      <nav><a href="/">Home</a></nav>
      <main>{children}</main>
    </div>
  );
}

Dynamic routes

Files named [param].tsx become dynamic segments:

// src/pages/video/[id].tsx → /video/abc123
export default function VideoPage({ params }) {
  return <h1>Video {params.id}</h1>;
}

Built-in components

Built-in components (Head, Link, Icon, Image, Script, Style) are recognized by the compiler by name — no import needed:

export default function Page() {
  return (
    <>
      <Head>
        <title>My Page</title>
        <meta name="description" content="A cool page" />
      </Head>
      <Icon name="lucide:heart" />
      <Image src="/photo.jpg" width={800} alt="Photo" />
      <Link href="/about">About</Link>
    </>
  );
}

<Image> compiles to a WebP-first <picture>: lossy WebP variants generated at build time with original-format fallback, responsive srcset/sizes, lazy/eager loading, blur placeholders, and aspect-ratio-based CLS mitigation. <Link> renders an SPA-enabled <a> with prefetching, replace, scroll, hash scrolling, and aria-current for the active route.

Next steps