Skip to main content
Task Manager — task list view with status filters and pagination

Overview

The Task Manager is the primary example app for @vertz/ui. It’s a multi-page SPA with CRUD operations, demonstrating how routing, data fetching, forms, theming, and context work together in a real application. It uses an in-memory mock API (no server or database required), so you can run it immediately and explore every Vertz feature in action.

Source code

Browse the full source on GitHub

Features demonstrated

FeatureAPIWhere in the app
RoutingdefineRoutes(), createRouter(), createLink()router.ts — 4 routes with typed params and search params
Data fetchingquery()task-list.tsx, task-detail.tsx — reactive queries with loading/error states
Formsform() with schema validationtask-form.tsx — per-field errors, submit state, progressive enhancement
Reactive statelet (compiler transforms to signals)task-list.tsx — status filter, all pages
Derived valuesconst (compiler transforms to computed)task-list.tsx — filtered tasks derived from query + filter
ContextcreateContext(), useContext()settings-context.ts — app-wide theme and default priority
ThemingconfigureTheme(), ThemeProvidertheme.ts, app.tsx — light/dark mode with contextual tokens
Stylingcss(), variants()components.ts — scoped styles and parameterized button/badge variants
Icons@vertz/iconsSidebar nav, filter buttons, pagination
URL stateuseSearchParams()task-list.tsx — pagination driven by URL search params
SSRServer-side rendering with hydrationFull SSR support out of the box

Pages

The main page displays tasks with status filtering (All / To Do / In Progress / Done) and URL-based pagination. Clicking a task navigates to its detail page.
Task list with filter tabs and pagination

Dark mode

The app supports light and dark themes via configureTheme() from @vertz/theme-shadcn. Theme switching is instant — CSS custom properties swap without re-rendering.
Task list in dark mode

Project structure

src
api
mock-data.ts
components
confirm-dialog.tsx
task-card.tsx
task-form.tsx
lib
settings-context.ts
types.ts
pages
create-task.tsx
settings.tsx
task-detail.tsx
task-list.tsx
styles
components.ts
theme.ts
app.tsx
entry-client.ts
router.ts
e2e
package.json

Key patterns

Reactive state with let

The compiler transforms let declarations into signals. Assignments become signal updates, and any JSX that references the variable re-renders automatically.
// task-list.tsx
let statusFilter: TaskStatus | 'all' = 'all';

// Clicking a filter button updates the signal — the UI reacts
<button onClick={() => { statusFilter = filter.value; }}>
  {filter.label}
</button>

Data fetching with query()

query() returns an object with reactive properties (data, loading, error). The compiler auto-wraps arguments in thunks, so changing search params automatically re-fetches.
// task-list.tsx
const sp = useSearchParams<{ page: number }>();
const tasksQuery = query(api.tasks.list({ page: sp.page, limit: 4 }));

// Derived value — compiler wraps in computed()
const filteredTasks = statusFilter === 'all'
  ? tasksQuery.data.items
  : tasksQuery.data.items.filter((t) => t.status === statusFilter);

Forms with form()

form() binds to an SDK method with schema validation. It provides per-field error signals and submit state for reactive form UIs.
// task-form.tsx
const taskForm = form(taskApi.create, {
  schema: createTaskSchema,
  onSuccess,
});

<form action={taskForm.action} method={taskForm.method} onSubmit={taskForm.onSubmit}>
  <input name={taskForm.fields.title} />
  <span>{taskForm.title.error}</span>
  <button disabled={taskForm.submitting}>Create Task</button>
</form>

Routing with defineRoutes()

Routes are defined declaratively with typed params and optional search param schemas. Pages access navigation via useRouter() — no prop threading.
// router.ts
const routes = defineRoutes({
  '/': {
    component: () => TaskListPage(),
    searchParams: s.object({ page: s.coerce.number().gte(1).default(1) }),
  },
  '/tasks/:id': {
    component: () => TaskDetailPage(),
    loader: async (ctx) => await api.tasks.get(ctx.params.id),
  },
});

export const appRouter = createRouter(routes, { serverNav: true });

Running the example

# Clone the repo and install dependencies
git clone https://github.com/vertz-dev/vertz.git
cd vertz
bun install

# Start the task manager dev server
cd examples/task-manager
bun run dev
The app starts at http://localhost:3000 with full SSR and HMR.

Available commands

bun run dev          # Development server with SSR + HMR
bun run build        # Production build
bun run preview      # Build + start production server
bun run typecheck    # TypeScript checking
bun test             # Unit tests
bun run e2e          # Playwright E2E tests