A simple, powerful React framework with RSC support powered by Vite and Hono.
- React Server Components - Stream UI from server to client with zero client JS by default
- Server Actions -
'use server'directive for type-safe server mutations - File-based Routing - Intuitive App Router style routing in
src/app/ - Hono API Routes - Fast, type-safe API routes with Hono
- Vite Powered - Lightning-fast dev server with instant HMR
- TypeScript - Full type inference out of the box
npx create-diezel my-app
cd my-app
pnpm install
pnpm devOpen http://localhost:3000 to see your app.
src/
├── app/ # File-based routes (RSC)
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page (/)
│ └── blog/
│ └── [slug]/
│ └── page.tsx # Dynamic route (/blog/:slug)
├── api/ # Hono API routes
│ └── index.ts
├── actions/ # Server actions
└── components/ # Shared components
Create pages by adding page.tsx files. The folder path becomes the URL. Pages are server components by default.
// src/app/users/[id]/page.tsx → /users/:id
import type { PageProps } from 'diezel'
export default async function UserPage({ params, searchParams }: PageProps) {
const user = await db.users.find(params.id)
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}Layouts wrap pages and persist across navigation:
// src/app/layout.tsx
import type { LayoutProps } from 'diezel'
export default function RootLayout({ children }: LayoutProps) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<title>My App</title>
</head>
<body>{children}</body>
</html>
)
}| File | URL |
|---|---|
app/page.tsx |
/ |
app/about/page.tsx |
/about |
app/blog/[slug]/page.tsx |
/blog/:slug |
app/docs/[...path]/page.tsx |
/docs/* |
Diezel uses Hono for API routes. Create your API in src/api/index.ts:
// src/api/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/health', (c) => c.json({ status: 'ok' }))
app.get('/users', async (c) => {
const users = await db.users.findMany()
return c.json(users)
})
export default appRoutes are accessible at /api/*. For example, /api/health or /api/users.
Split your API into modules:
// src/api/users.ts
import { Hono } from 'hono'
export const users = new Hono()
users.get('/', (c) => c.json(getUsers()))
users.get('/:id', (c) => c.json(getUser(c.req.param('id'))))
users.post('/', async (c) => {
const data = await c.req.json()
return c.json(createUser(data), 201)
})
// src/api/index.ts
import { Hono } from 'hono'
import { users } from './users'
const app = new Hono()
app.route('/users', users) // /api/users/*
export default appUse 'use server' for type-safe mutations from client components:
// src/actions/users.ts
'use server'
export async function createUser(data: { name: string; email: string }) {
return await db.users.create(data)
}
export async function deleteUser(id: number) {
await db.users.delete(id)
return { success: true }
}// src/components/UserForm.tsx
'use client'
import { createUser } from '../actions/users'
export function UserForm() {
async function handleSubmit(formData: FormData) {
const user = await createUser({
name: formData.get('name') as string,
email: formData.get('email') as string,
})
console.log('Created:', user.id)
}
return (
<form action={handleSubmit}>
<input name="name" placeholder="Name" />
<input name="email" placeholder="Email" type="email" />
<button type="submit">Create</button>
</form>
)
}Mark components that need interactivity with 'use client':
// src/components/Counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}Client components are automatically hydrated on the client.
Configure Diezel in your vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { diezel } from 'diezel/vite'
export default defineConfig({
plugins: [diezel(), react()],
})diezel dev # Start dev server
diezel build # Build for production
diezel start # Start production server- Node.js 18+
- pnpm, npm, or yarn
MIT