Getting Started

Learn how to set up a Carats project and get a server-rendered application running.

Requirements

Carats uses Bun for package management and for running the development server. Bun transpiles TSX on the fly, so there is no separate compile step while developing. Node.js 20+ also works for production builds, but Bun is the supported path.

Quick start

The fastest way to start is the official project generator. It writes a complete, working Carats application with facets, culets, a shared layout, SASS design tokens and tests, so every feature described in these docs is already wired up and can be read as working code.

Create a new project

bun create carats my-carats-app
cd my-carats-app

`bun create carats` resolves the published `create-carats` package and runs its `create-carats` binary, which writes a complete Carats project into the directory you name. Pass no directory to scaffold into the current one.

Install dependencies and start the dev server

bun install
bun run dev

The server listens on `http://localhost:5173`. The generated project ships with a dashboard, a market, a portfolio, a trade page, a shared layout, a not-found page and an error page, so every feature described in these docs is already wired up and can be read as working code.

Manual setup

If you prefer to start from an empty project, create the folder structure below and install the dependencies yourself.

Folder structure

Create the following folders and files. The locations are fixed because the framework resolves them by convention.

src/
├── app.ts
├── vite-env.d.ts
├── dto/
│   ├── user.d.ts
│   └── trade-data.d.ts
├── client/
│   ├── index.html
│   ├── entrypoint.ts
│   ├── facets.tsx
│   ├── base.sass
│   ├── components/
│   │   ├── SearchInput.tsx
│   │   └── search-bar.sass
│   └── pages/
│       ├── _layout/index.tsx
│       ├── _error/index.tsx
│       ├── _not_found/index.tsx
│       ├── home/index.tsx
│       ├── market/index.tsx
│       ├── profile/index.tsx
│       └── trade/index.tsx
└── server/
    ├── entrypoint.ts
    └── culets.ts
public/
vite.config.client.ts
vite.config.server.ts
vitest.config.ts
tsconfig.json
package.json

Install dependencies

Carats is split into small packages. The runtime dependencies below are the ones used by a full-stack application; the dev dependencies are needed for JJSX transpilation, SASS and type checking.

Runtime dependencies

bun add @carats/core @carats/render @carats/hooks @carats/csr
bun add @carats/ssr @carats/express @carats/ssg
bun add express compression sirv

Dev dependencies

bun add -d jjsx vite sass typescript
bun add -d @types/node @types/express @types/compression
bun add -d vitest @vitest/coverage-v8 @fetch-mock/vitest cross-env

`@carats/express` requires Express 5, so the major version is not optional.

Configuration

TypeScript configuration

Carats requires a tsconfig.json at the root of the project. The types, jsx, jsxFactory and jsxFragmentFactory options are what teach TypeScript about JJSX, and typeRoots is what makes the global DTO interfaces in src/dto resolvable.

{
  "compilerOptions": {
    "target": "es2022",
    "useDefineForClassFields": true,
    "module": "esnext",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "types": ["jjsx"],
    "typeRoots": ["./src/dto", "./node_modules"],
    "skipLibCheck": true,
    "jsx": "react",
    "jsxFactory": "JJSX.jsxFactory",
    "jsxFragmentFactory": "JJSX.fragmentFactory",

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["src"]
}

`typeRoots` does double duty. It points at `src/dto` so the global DTO interfaces are picked up as declaration files, and it keeps `./node_modules` on the search path so `types: ["jjsx"]` resolves, since `jjsx` does not live under `@types`.

Client Vite configuration

The client build runs in middleware mode because the Express server owns the HTTP listener. appType: 'custom' disables Vite's own HTML fallback, since Carats renders the HTML template itself.

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  publicDir: path.resolve(import.meta.dirname, 'public'),
  root: path.resolve(import.meta.dirname, 'src/client'),
  base: '/',
  appType: 'custom',
  server: {
    middlewareMode: true
  },
  build: {
    outDir: path.resolve(import.meta.dirname, 'dist/client'),
    emptyOutDir: true,
    manifest: true,
    minify: true,
    rollupOptions: {
      treeshake: true
    }
  }
})

Server Vite configuration

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  publicDir: false,
  build: {
    ssr: path.resolve(__dirname, 'src/server/entrypoint.ts'),
    outDir: path.resolve(__dirname, 'dist/server'),
    emptyOutDir: true,
    minify: true,
    rollupOptions: {
      treeshake: true
    }
  }
})

Package.json scripts

These scripts are the supported entry points for developing, building and previewing a Carats application.

{
  "scripts": {
    "dev": "bun --inspect=6499 src/app.ts",
    "build": "bun build:server && bun build:client",
    "build:client": "vite build --config vite.config.client.ts",
    "build:server": "vite build --config vite.config.server.ts",
    "build:static": "cross-env NODE_ENV=production carats-ssg",
    "preview": "cross-env NODE_ENV=production bun src/app.ts",
    "test": "vitest --run --coverage"
  }
}

The main application file must live at `src/app.ts`.

Running the app

bun run dev starts the app with the Bun inspector on port 6499. bun run build bundles the server and the client, and bun run preview serves the production build. bun run build:static prerenders the app to static HTML.

Project structure

A typical Carats project has the following structure:

PathDescription
src/app.tsMain application entry point. Mounts the Carats middleware on Express and starts the HTTP server.
src/vite-env.d.tsVite client type reference. Lives at src/vite-env.d.ts, not inside src/client.
src/client/index.htmlHTML template rendered by the server. Must contain the <!--app-head--> and <div id="app"><!--app-html--></div> placeholders.
src/client/entrypoint.tsClient entrypoint. Mounts the facets on the browser and starts client-side rendering.
src/client/facets.tsxRoute definitions, shared by the client and the server entrypoint.
src/client/base.sassGlobal stylesheet holding the design tokens and base resets.
src/client/pages/Page components. Each subfolder exports a default component from index.tsx and may import its own SASS file.
src/client/components/Shared components used by pages.
src/server/entrypoint.tsServer entrypoint for SSR. Exports defineServerEntry(facets) and seats culets.
src/server/culets.tsCulets, the server-side data functions used by burnished components.
src/dto/Global TypeScript interfaces. Included through typeRoots so culet(...) resolves without an import.
public/Static assets served from the site root.

Next steps

Read Core Concepts to understand how JJSX, facets, culets and server props fit together, then look at the Components and Server-Side Rendering pages.