Configuration
One file on the server sets the navigation, the dashboard and the brand. Two components in your app mount the result.
The panel is up and it looks generic. The React is not where you fix that. The admin asks the server what to render, so navigation and dashboard are data.
One file on the server
Put a file at config/admin.ts under your server directory. Codegen finds it
and the app carries it as config.admin. Import adminConfig from
#questpie/factories, the same generated factory your collections use.
| Key | What it sets |
|---|---|
branding | Panel name, logo, tagline, favicon |
sidebar | The navigation sections, and what sits in them |
dashboard | What /admin itself shows |
locale | Which languages the admin interface offers |
shell | A second side rail holding a component of yours |
sidebarMode | Whether module navigation merges in, or yours replaces it |
Every key is optional. A file with one key in it is a valid file.
import { adminConfig } from "#questpie/factories";
export default adminConfig({
branding: {
name: "Acme Studio",
logo: { src: "/logo-light.svg", srcDark: "/logo-dark.svg" },
},
sidebar: {
sections: [{ id: "content", title: "Content" }],
items: [
{ sectionId: "content", type: "collection", collection: "posts" },
{ sectionId: "content", type: "global", global: "siteSettings" },
],
},
dashboard: {
title: "Operations",
items: [
{
id: "published",
type: "stats",
collection: "posts",
label: "Published",
filter: { published: true },
},
],
},
});questpie generate # once, so the generator picks the new file upReload /admin. The panel is called Acme Studio, the sidebar has a Content
section with two entries, and the dashboard shows one counter. questpie dev
regenerates on later edits, so that is the only manual generate.
The sidebar is a contribution
Sections and items are two flat lists. An item names its section with
sectionId instead of nesting inside it. Modules write the same two lists, so
their navigation lands beside yours instead of replacing it.
@questpie/admin contributes a section called administration, holding user
and assets. Turn the audit module on and its log joins that same section.
Declare a section with an id someone else already used and your title wins.
type | Extra key | Points at |
|---|---|---|
collection | collection | A registered collection name |
global | global | A registered global name |
page | pageId | A page in src/questpie/admin/pages/ |
link | href | Any URL, rendered with your LinkComponent |
divider | none | A rule between two items |
position: "start" prepends an item rather than appending it. Labels and icons
fall back to the collection's own, so set those once with .admin().
sidebarMode: "replace" throws away every module contribution and keeps only
your sidebar. Users and assets disappear too, until you list them yourself.
The sidebar is a list, not a mirror
List every collection you want in the nav. Once any section holds an item, collections you left out are not appended for you. The admin module always contributes two, so this is true from the first line you write.
Branding
Four fields, all optional, all of them content. No colours live here.
| Field | Where it shows |
|---|---|
name | Sidebar header, browser tab title, sign-in screen |
logo | Beside the name in the sidebar, and on the sign-in screen |
tagline | One line under the logo on the auth screens |
favicon | A URL. The client swaps the <head> icon once config loads |
logo takes a URL string, or { src, srcDark } for one image per theme. It
also takes a component reference, if you ship the mark as a component.
Branding is the only part of the config an unauthenticated visitor receives. That is what puts your name on the login screen before anyone signs in. Colours, fonts and radius are CSS variables. See Theming.
Mount it
AdminLayoutProvider wraps the whole admin route and supplies the sidebar, the
theme and the data clients. AdminRouter sits inside it and renders one screen.
import { AdminLayoutProvider } from "@questpie/admin/client";
import { Outlet, useLocation } from "@tanstack/react-router";
import { authClient } from "@/lib/auth-client";
import { client } from "@/lib/client";
import { queryClient } from "@/lib/query-client";
import { admin } from "@/questpie/admin/admin";
function AdminLayout() {
const location = useLocation();
return (
<AdminLayoutProvider
admin={admin}
client={client}
queryClient={queryClient}
authClient={authClient}
LinkComponent={AdminLink}
activeRoute={location.pathname}
basePath="/admin"
useServerTranslations
>
<Outlet />
</AdminLayoutProvider>
);
}| Prop | What it wants |
|---|---|
admin | The generated client, src/questpie/admin/.generated/client |
client | Your typed API client. Every screen reads through it |
queryClient | Optional. Left out, the admin makes its own |
authClient | Better Auth client. Passing it turns the auth guard on |
LinkComponent | Your router's link, so nav does not full-page reload |
activeRoute | The current pathname, used to highlight the active item |
basePath | Where the admin is mounted. /admin by default |
The catch-all route below it hands AdminRouter the segments after the base
path. /admin/collections/posts becomes ["collections", "posts"].
<AdminRouter segments={segments} navigate={navigate} basePath="/admin" />`AdminRouter` does not take the admin object
segments and navigate are the two it needs. It reads the admin, the client
and the query client from the provider above it. Mount it outside
AdminLayoutProvider and it has nothing to read.
create-questpie writes both files. The Next version is the same two
components, with usePathname and useParams in place of the TanStack hooks.
Interface language
locale sets which languages the admin chrome offers. Leave it out and the
server offers all eight it ships with: cs, de, en, es, fr, pl, pt, sk. The user
menu in the sidebar footer gets a switcher once there is more than one.
locale: { locales: ["en", "sk"], defaultLocale: "en" },Pass useServerTranslations on the provider so the client asks the server for
that list. Content locales are a separate setting and live in config/app.ts.
Where each topic lives
| Topic | Page |
|---|---|
| Widget types, loaders, sections and tabs | Dashboard |
| Colours, fonts, dark mode, chrome components | Theming |
| Labels, icons, columns, form layout | Collections and globals |
| Your own pages, views and widgets | Custom views |
| Who may sign in | Authentication |
Next
Dashboard is the one key worth its own page. Nine widget types, and a server loader for numbers none of them can count.