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:
| Path | Description |
|---|---|
src/app.ts | Main application entry point. Mounts the Carats middleware on Express and starts the HTTP server. |
src/vite-env.d.ts | Vite client type reference. Lives at src/vite-env.d.ts, not inside src/client. |
src/client/index.html | HTML template rendered by the server. Must contain the <!--app-head--> and <div id="app"><!--app-html--></div> placeholders. |
src/client/entrypoint.ts | Client entrypoint. Mounts the facets on the browser and starts client-side rendering. |
src/client/facets.tsx | Route definitions, shared by the client and the server entrypoint. |
src/client/base.sass | Global 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.ts | Server entrypoint for SSR. Exports defineServerEntry(facets) and seats culets. |
src/server/culets.ts | Culets, the server-side data functions used by burnished components. |
src/dto/ | Global TypeScript interfaces. Included through typeRoots so culet |
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.