Advanced
Examples
Real-world examples and patterns for building with Manic.
Examples
Practical patterns you can drop into a Manic application. Every snippet assumes a fresh project created with bun create manic and uses the official manicjs exports only.
Counter (State)
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count is {count}
</button>
);
}Data Fetching from an API Route
Define the API endpoint:
import { Hono } from 'hono';
const app = new Hono();
app.get('/', c => c.json({ data: [1, 2, 3] }));
export default app;Consume it from a page:
import { useEffect, useState } from 'react';
export default function DataPage() {
const [items, setItems] = useState<number[]>([]);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(json => setItems(json.data));
}, []);
return (
<ul>
{items.map(i => (
<li key={i}>{i}</li>
))}
</ul>
);
}Type-safe RPC with Hono
Share the Hono app type between server and client for end-to-end type safety:
import { Hono } from 'hono';
const app = new Hono().get('/', c => c.json({ users: ['Ada', 'Linus'] }));
export default app;
export type AppType = typeof app;import { hc } from 'hono/client';
import type { AppType } from '../../api/users';
export const usersClient = hc<AppType>('/api/users');import { useEffect, useState } from 'react';
import { usersClient } from './~lib/client';
export default function UsersPage() {
const [users, setUsers] = useState<string[]>([]);
useEffect(() => {
usersClient.index
.$get()
.then(res => res.json())
.then(json => setUsers(json.users));
}, []);
return <ul>{users.map(u => <li key={u}>{u}</li>)}</ul>;
}View Transitions Between Pages
Use the <ViewTransitions> factory to mark elements that should animate across navigations:
import { Link, ViewTransitions } from 'manicjs';
export default function ItemList({ items }: { items: { id: string; thumb: string }[] }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
<Link to={`/items/${item.id}`}>
<ViewTransitions.img name={`item-${item.id}`} src={item.thumb} alt="" />
</Link>
</li>
))}
</ul>
);
}import { useRouter, ViewTransitions } from 'manicjs';
export default function ItemDetail() {
const { params } = useRouter();
return (
<ViewTransitions.img
name={`item-${params.id}`}
src={`/items/${params.id}/full.jpg`}
alt=""
/>
);
}Manic invokes document.startViewTransition() automatically — no extra wiring required.
Protected Page
import { useEffect, useState } from 'react';
import { useRouter } from 'manicjs';
export default function AdminPage() {
const { navigate } = useRouter();
const [me, setMe] = useState<{ isAdmin: boolean } | null>(null);
useEffect(() => {
fetch('/api/me')
.then(res => res.json())
.then(setMe);
}, []);
useEffect(() => {
if (me && !me.isAdmin) navigate('/', { replace: true });
}, [me, navigate]);
if (!me?.isAdmin) return null;
return <h1>Admin Dashboard</h1>;
}Theming with useTheme
import { createRoot } from 'react-dom/client';
import { Router, ThemeProvider } from 'manicjs';
createRoot(document.getElementById('root')!).render(
<ThemeProvider>
<Router />
</ThemeProvider>,
);import { ThemeToggle, useTheme } from 'manicjs';
export function Toggle() {
const { resolvedTheme } = useTheme();
return (
<ThemeToggle aria-label={`Switch from ${resolvedTheme} mode`} />
);
}See Also
- Routing — page conventions.
- Server & API — Hono endpoints.
- Plugins — Tailwind, MDX, MCP, sitemap.