QUESTPIE
Admin

Configuration

One file on the server sets the navigation, the dashboard and the brand. Two components in your app mount the result.

View markdown

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.

KeyWhat it sets
brandingPanel name, logo, tagline, favicon
sidebarThe navigation sections, and what sits in them
dashboardWhat /admin itself shows
localeWhich languages the admin interface offers
shellA second side rail holding a component of yours
sidebarModeWhether module navigation merges in, or yours replaces it

Every key is optional. A file with one key in it is a valid file.

src/questpie/server/config/admin.ts
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 up

Reload /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.

typeExtra keyPoints at
collectioncollectionA registered collection name
globalglobalA registered global name
pagepageIdA page in src/questpie/admin/pages/
linkhrefAny URL, rendered with your LinkComponent
dividernoneA 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.

FieldWhere it shows
nameSidebar header, browser tab title, sign-in screen
logoBeside the name in the sidebar, and on the sign-in screen
taglineOne line under the logo on the auth screens
faviconA 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.

src/routes/admin.tsx
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>
	);
}
PropWhat it wants
adminThe generated client, src/questpie/admin/.generated/client
clientYour typed API client. Every screen reads through it
queryClientOptional. Left out, the admin makes its own
authClientBetter Auth client. Passing it turns the auth guard on
LinkComponentYour router's link, so nav does not full-page reload
activeRouteThe current pathname, used to highlight the active item
basePathWhere 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"].

src/routes/admin/$.tsx
<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

TopicPage
Widget types, loaders, sections and tabsDashboard
Colours, fonts, dark mode, chrome componentsTheming
Labels, icons, columns, form layoutCollections and globals
Your own pages, views and widgetsCustom views
Who may sign inAuthentication

Next

Dashboard is the one key worth its own page. Nine widget types, and a server loader for numbers none of them can count.

On this page