# Configuration (/docs/admin/configuration)

---
title: Configuration
description: One file on the server sets the navigation, the dashboard and the brand. Two components in your app mount the result.
kind: guide
package: "@questpie/admin"
---

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.

```ts title="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 },
			},
		],
	},
});
```

```bash
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.

| `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.

<Callout type="warn" title="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.
</Callout>

## 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](/docs/admin/configuration/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.

```tsx title="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>
	);
}
```

| 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"]`.

```tsx title="src/routes/admin/$.tsx"
<AdminRouter segments={segments} navigate={navigate} basePath="/admin" />
```

<Callout type="warn" title="`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.
</Callout>

`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.

```ts
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](/docs/admin/configuration/dashboard)   |
| Colours, fonts, dark mode, chrome components | [Theming](/docs/admin/configuration/theming)       |
| Labels, icons, columns, form layout          | [Collections and globals](/docs/admin/collections) |
| Your own pages, views and widgets            | [Custom views](/docs/admin/custom-views)           |
| Who may sign in                              | [Authentication](/docs/admin/auth)                 |

## Next

**[Dashboard](/docs/admin/configuration/dashboard)** is the one key worth its own
page. Nine widget types, and a server loader for numbers none of them can count.
