SSR-first JSX for Node.js
File-based routing. Async server components. Zero client JavaScript by default. Opt in to fine-grained reactivity one island at a time.
npx davaux create my-app
cd my-app && npm install
npm run dev
File-based routing
The filename encodes the URL and HTTP method. blog/[slug].page.tsx becomes GET /blog/:slug. No config, no boilerplate.
Async JSX everywhere
Any component can await data — no getServerSideProps, no loader functions. The server renders the full tree before sending a byte.
Islands + signals
Write a component once. The framework compiles it for both server and client automatically. Fine-grained signals update only the exact DOM nodes that changed — no VDOM, no re-renders.
Layouts that nest
Drop _layout.tsx in any directory. Layouts compose from outermost to innermost automatically. Each layout gets ctx.head after the page has run, so titles and meta tags are always accurate.
Middleware that scopes
_middleware.ts applies to routes in its directory. src/middleware.ts runs on every request, including 404s. Auth, logging, rate limiting — each wired once and applied exactly where you want it.
Co-located form actions
Export an action from any page file to handle its form submissions. No separate API route needed — validation, mutation, and re-render all live in one place.
Head management
Set ctx.head.title, ctx.head.description, and arbitrary meta tags from any page or layout. Values are always resolved by the time the root layout renders.
Partial updates
Stream slow content into an already-sent HTML shell with ctx.defer(). Built on the browser's native Declarative Partial Updates API — no client JavaScript required.
Custom error pages
Drop _error.tsx in your routes directory to handle any 404 or 500 with your own design. Error pages inherit layouts and have full access to ctx.
Static generation built in
davaux static pre-renders every page to plain HTML. Dynamic routes declare their paths with getStaticPaths. Deploy to any CDN — no Node.js required at runtime.
A page in 10 lines
// src/routes/blog/[slug].page.tsx
import { definePage, type ExtractParams } from 'davaux'
import { getPost } from '~/data/posts'
export default definePage<ExtractParams<'blog/[slug]'>>(async (ctx) => {
const post = await getPost(ctx.params.slug)
ctx.head.title = post.title
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
})