Getting Started

Davaux is an SSR-first JSX framework for Node.js. It gives you file-based routing, server-rendered JSX pages, and zero client-side JavaScript by default. When you need interactivity, you opt in to islands — isolated client components that hydrate independently while the rest of the page stays as plain HTML.

Requirements

  • Node.js 22 or later
  • npm 10+

Scaffold a new project

The fastest way to get started is with npx davaux create:

npx davaux create my-app
cd my-app
npm install

The scaffolded project includes a basic file structure, a davaux.config.ts, and example routes so you can see the conventions in action right away.

Project structure

A typical Davaux project looks like this:

my-app/
├── davaux.config.ts       # Framework configuration
├── package.json
├── tsconfig.json
└── src/
    ├── islands/           # Client-side interactive components
    │   └── Counter.tsx
    └── routes/            # File-based routing root
        ├── _layout.tsx    # Root layout (HTML shell)
        ├── index.page.tsx # → GET /
        └── about.page.tsx # → GET /about

Everything under src/routes/ becomes a URL. Files named _layout.tsx, _middleware.ts, and _error.tsx are framework-reserved and do not map to URLs.

Running the dev server

npm run dev
# or: npx davaux dev

The dev server starts on http://localhost:3000 by default and watches your source files for changes. When a route file changes the server reloads automatically; when a new route is created or deleted the router re-scans.

Creating your first page

Pages are TypeScript/TSX files that export a default handler via definePage. The handler receives a request context object and returns JSX (or a redirect):

// src/routes/hello.page.tsx
import { definePage } from 'davaux'

export default definePage((ctx) => {
  const name = ctx.query.get('name') ?? 'world'

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>This page is server-rendered.</p>
    </div>
  )
})

Visit http://localhost:3000/hello?name=Davaux to see it rendered.

Page handlers can be async — you can fetch data, read from a database, or call any async API:

import { definePage } from 'davaux'

export default definePage(async (ctx) => {
  const res = await fetch('https://api.example.com/posts')
  const posts = await res.json() as { id: number; title: string }[]

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
})

Setting the page title

Use ctx.head to set the title and description for the current page. Your root layout reads these values to render the appropriate <title> and <meta> tags:

import { definePage } from 'davaux'

export default definePage((ctx) => {
  ctx.head.title = 'Hello — My App'
  ctx.head.description = 'A warm greeting page.'

  return <h1>Hello!</h1>
})

Building for production

Server-side rendering (SSR):

npm run build    # compiles to dist/
davaux start     # serves dist/ with Node.js

Static site generation (SSG):

davaux static    # pre-renders all pages to out/

The out/ directory is a fully static tree of HTML files that can be deployed to any static host — no Node.js required at runtime.