Admin
The admin is a data workspace generated from your schema. The people who own the data get a real interface, instead of a database client or a spreadsheet that drifts.
The admin is generic over your schema. It is not built for blog posts, or for products, or for any one kind of content. It reads what you declared and renders that. Add a field and it turns up in the form on the next generate. Nobody keeps a second copy of your model in a React app.
This group covers how far you can move it, and where the built-in behaviour stops.
Turn it on
The admin is a module with two halves. Register each one.
import { adminModule } from "@questpie/admin/modules/admin";
export default [adminModule] as const;import { adminClientModule } from "@questpie/admin/client/modules/admin";
export default [adminClientModule] as const;questpie generateThe server half contributes collections, routes, views, components and two field
types, f.richText() and f.blocks(). The client half turns the server's
references into React screens. Generate writes
src/questpie/admin/.generated/client.ts, and your /admin route mounts it.
create-questpie writes all of this for you.
adminModule also pulls in starterModule. That is where the Better Auth tables
and the assets upload collection come from. A fresh panel has users and a media
library on day one.
Two runtimes, not four
The admin needs a render layer. create-questpie turns it on by default for
TanStack Start and Next, and rejects it on Hono and Elysia. Those two stop at
the API and the typed client.
What you get before you configure anything
| URL | What is there |
|---|---|
/admin | The dashboard |
/admin/collections/posts | The list, with filters, sorting and a column picker |
/admin/collections/posts/create | An empty form |
/admin/collections/posts/:id | That row's form |
/admin/globals/siteSettings | The one row a global holds |
/admin/login | Sign in |
/admin/setup | Creates the first admin, while none exists |
The URL uses the registered name, not the file name. So collection("blog-posts")
sits at /admin/collections/blogPosts.
A form with no form config generates its fields from the collection. A list with
no list config picks its own columns. The title first, then up to six short
fields, then createdAt. Relations, uploads, rich text, JSON, objects, arrays
and textareas stay out of that default, because they are wide or expensive to
load. Every one of them is still in the column picker.
Who may sign in
One rule decides it. session.user.role === "admin". A signed-in user without
that role is not an admin user, and every admin RPC route refuses them.
Read access filters the rest. A collection your session cannot read is missing from the config the client receives, so it is missing from the sidebar too.
The first admin is a chicken-and-egg problem. /admin/login asks the server
whether any user holds the admin role. If none does, it sends you to
/admin/setup, which creates one. Once one exists, setup refuses to create any
more.
Where each topic lives
| Topic | Page |
|---|---|
| Labels, icons, list columns, form layout, actions | Collections and globals |
| Sidebar, dashboard, branding, theming, mounting | Configuration |
| Your own views, widgets, pages and blocks | Custom views |
| Better Auth, roles, setup and invitations | Authentication |
| Who changed what, and when | Audit |
| Stacked content a person arranges | Blocks |
| How a module contributes all of the above | Modules |
Next
Collections and globals is the page to read first.
It covers .admin(), .list(), .form(), .preview() and .actions(), the
five methods @questpie/admin adds to a collection.