Getting started
From zero to a working /admin route in under a minute.
flowpanel turns a single typed config into a complete /admin route for
your Next.js app. This guide walks you from a fresh install to a working
dashboard.
Just want to click around? A public read-only instance of the
freelance-radarexample is live at demo.flowpanel.dev — no install required.Prefer the source? Open the
with-clerkexample in StackBlitz to edit a full flowpanel config in your browser.
Requirements. Next.js 15+ (App Router), React 19, Node 20+, and either Drizzle 0.30+ or Prisma 5+. The Pages router is not supported.
Install
Add the package to your existing Next.js project. flowpanel detects your ORM automatically — there's nothing to configure at install time.
pnpm add @flowpanel/kitInitialize
Run flowpanel init from your project root. The CLI inspects your
package.json, picks Drizzle or Prisma, and writes the wiring files.
$ pnpm flowpanel init
→ Detected stack: Next.js 15 · TypeScript · Drizzle
→ wrote flowpanel.config.ts
→ wrote app/admin/[[...slug]]/page.tsx
→ wrote app/api/flowpanel/[...route]/route.ts
→ wrote app/api/flowpanel/stream/route.tsThe generated config is the entire admin panel. Every resource you list becomes a typed CRUD page; everything you don't list stays out.
The config file
Open flowpanel.config.ts. You'll see something like this (Drizzle
shown; the Prisma variant is shape-identical save the adapter import):
import { defineAdmin, resource } from "@flowpanel/kit";
import { drizzleAdapter } from "@flowpanel/kit/drizzle";
import { db } from "@/server/lib/db";
import * as schema from "@/server/lib/db/schema";
import { getSession } from "@/server/lib/auth";
declare module "@flowpanel/core" {
interface FlowpanelTypes {
db: typeof db;
}
}
export default defineAdmin({
adapter: drizzleAdapter({ db, schema }),
auth: {
session: getSession,
role: (s) => (s as { user?: { role?: string } } | null)?.user?.role ?? "guest",
requireRole: "admin",
},
realtime: { driver: "memory" },
resources: [
resource(schema.users, {
columns: ["email", "role", "createdAt"],
}),
resource(schema.orders, {
columns: ["id", "status", "total"],
filters: [{ field: "status", type: "select" }],
}),
],
});Each resource() call describes one table you'll manage in the admin.
The first argument is the Drizzle table (or, for Prisma, the model name
as a string — e.g. resource("User", { ... })). Columns, filters, and
actions are type-checked against the row shape inferred from your
schema.
The declare module block binds typeof db into WidgetContext, so
ctx.db in dashboards and actions is your real, typed client.
Mount the route
flowpanel init already wrote two files:
// app/admin/[[...slug]]/page.tsx
import { Flowpanel } from "@flowpanel/kit/next";
import config from "@/flowpanel.config";
export default Flowpanel(config);// app/api/flowpanel/[...route]/route.ts
import { handlers } from "@flowpanel/kit/next";
import config from "@/flowpanel.config";
export const { GET, POST } = handlers(config);
export const runtime = "nodejs";The page renders every /admin/... URL via a single catch-all RSC. The
API route handles the drawer fetch and bulk-action submissions. A third
file, app/api/flowpanel/stream/route.ts, mounts the SSE channel for
realtime — generated automatically by init.
Run
Start your dev server and open localhost:3000/admin.
pnpm devYou'll see a typed dashboard with one page per resource, full CRUD, pagination, and filters — driven entirely by the config you just wrote.
Next steps
- Tweak rendering and behavior in Configuration
- See how Adapters connect flowpanel to your data
- Replace any component via the Theme slots layer
- Eject a resource into source code when you outgrow the config layer