# Project structure

The folders of AdminKit for Next.js: App Router routes, components, data, hooks and Sass, and how to add a page with its title and sidebar link.

AdminKit for Next.js follows the App Router conventions: every URL is a folder in `src/app/` with a `page.tsx`. This page shows where things live, how a page gets its title and how to add a page of your own.

The free and PRO versions use the same folders. AdminKit PRO has more pages, components and plugins in them.

```text
├── public/                 images and other files served as they are
├── scripts/                serve.mjs (npm start) and stats.mjs (npm run stats)
├── src/
│   ├── app/                the routes
│   │   ├── layout.tsx      <html>, the head scripts and the stylesheet
│   │   ├── (dashboard)/    pages with the sidebar, navbar and footer; page.tsx is /
│   │   └── (auth)/         sign-in, sign-up and error pages, centered
│   ├── components/
│   │   ├── bootstrap/      Modal, Dropdown, Offcanvas, ... (see Components)
│   │   ├── plugins/        ChartJs, VectorMap, ... (see Charts)
│   │   ├── navbar/         the top bar and its menus
│   │   ├── sidebar/        the sidebar and its menu
│   │   ├── settings/       the settings panel (PRO)
│   │   ├── ui/             PageTitle, CountUp, Skeleton, Footer
│   │   └── pages/          what one page uses, in pages/<section>/<page>/
│   ├── contexts/           LayoutProvider: the sidebar toggle
│   ├── data/               pages.json, navigation.ts, stats.json and the other app data
│   ├── hooks/              useColorMode, useTheme, useConfig (PRO), ...
│   ├── layouts/            DashboardLayout, AuthLayout and PageEffects
│   ├── lib/                config.ts, head-scripts.ts, site.ts
│   ├── mocks/              the demo data of the pages
│   └── scss/               app.scss and the Sass partials
└── next.config.ts          the static export and BASE_PATH
```

`src/app/` holds only `page.tsx` and `layout.tsx` files. The route groups `(dashboard)` and `(auth)` pick the layout and are not part of the URL: `src/app/(dashboard)/pages-settings/page.tsx` is `/pages-settings`.

## Pages

A page is a server component. It exports its metadata and renders Bootstrap markup and components:

```tsx title="src/app/(dashboard)/pages-blank/page.tsx"
import { PageTitle } from "@/components/ui/page-title";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/pages-blank");

export default function Page() {
  return (
    <>
      <PageTitle title="Blank Page" />

      <div className="row">
        <div className="col-12">
          <div className="card">
            <div className="card-header">
              <h2 className="card-title mb-0">Empty card</h2>
            </div>
            <div className="card-body"></div>
          </div>
        </div>
      </div>
    </>
  );
}
```

`pageMetadata()` reads the title and meta description from `src/data/pages.json`.

A server component renders at build time, because the template is a static export, and sends HTML without its JavaScript. Put `"use client"` on the smallest component that needs state, effects or the browser, not on the page. The page stays a server component, so its `metadata` keeps working and less JavaScript reaches the browser. The interactive components in `components/bootstrap/` and `components/plugins/` are client components already, so a page can render them directly. `Tooltip` and `Popover` are the exception: they clone their child element, so use them inside a client component.

Demo data lives in `src/mocks/`. Replace it with your own data. With the static export, data that the page fetches is read once, at build time. Data that changes while people use the app belongs in a client component that fetches it in the browser.

## Add a page

1. Create the route, for example `src/app/(dashboard)/reports/page.tsx`, from the blank page above with `pageMetadata("/reports")`.
2. Add it to `src/data/pages.json`:

   ```json
   {
     "path": "/reports",
     "title": "Reports",
     "layout": "dashboard",
     "description": "Monthly reports."
   }
   ```

3. Link it in the sidebar menu in `src/data/navigation.ts` (see [Sidebar](/nextjs/docs/layout/sidebar/)).
4. Run `npm run stats`. It updates the counts in `src/data/stats.json`, and `npm run lint` fails while a route is missing from `pages.json` or the counts are out of date.

A page in `(auth)` gets the centered layout of the sign-in page instead. Give it `"layout": "auth"` in `pages.json`.

## Page title

`PageTitle` puts the name of the page at the top, so people know where they are. It renders the `<h1>` of every dashboard page, in the free and PRO versions. The [blank page demo](https://nextjs-demo.adminkit.io/pages-blank) shows it.

`PageTitle` has no state and no `"use client"`, so a page renders it as a server component. The document `<title>` in the browser tab is separate. The App Router builds it from the page's `metadata` export, as [Document title](#document-title) explains.

```html
<h1 class="h3 mb-3">Reports</h1>
```
```tsx code title="src/app/(dashboard)/reports/page.tsx"
import { PageTitle } from "@/components/ui/page-title";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/reports");

export default function ReportsPage() {
  return (
    <>
      <PageTitle title="Reports" />
      {/* the page */}
    </>
  );
}
```

The preview is the AdminKit PRO output. The free version adds a badge.

`PageTitle` is in `src/components/ui/page-title.tsx`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` | | The heading text |
| `pro` (PRO) | `boolean` | | Adds a "Pro Component" badge that links to the AdminKit pricing page |
| `badge` (free version) | `string` | `"Get more demos"` | The text of the badge that links to `/upgrade-to-pro` |

### Badges

The badges belong to the demo. In the free version, every `PageTitle` has a badge that links to the upgrade page of the demo, with `next/link`:

```html
<div class="mb-3">
  <h1 class="h3 d-inline align-middle">Chart.js</h1>
  <a class="badge bg-dark text-light ms-2 d-print-none" href="#">Get more chart examples</a>
</div>
```
```tsx code
<PageTitle title="Chart.js" badge="Get more chart examples" />
```

For your own app, replace the component with the heading alone:

```tsx title="src/components/ui/page-title.tsx"
export function PageTitle({ title }: { title: string }) {
  return <h1 className="h3 mb-3">{title}</h1>;
}
```

In AdminKit PRO, `pro` marks the demo pages of PRO components. Your own pages do not need it.

```html
<div class="mb-3">
  <h1 class="h3 d-inline align-middle">ApexCharts</h1>
  <a class="badge bg-primary ms-2" href="https://adminkit.io/pricing/" target="_blank">Pro Component <i class="fas fa-fw fa-external-link-alt"></i></a>
</div>
```
```tsx code
<PageTitle title="ApexCharts" pro />
```

### Document title

The title in the browser tab comes from the page, not from `PageTitle`. Each page exports `metadata = pageMetadata("/<route>")`. `pageMetadata`, in `src/lib/site.ts`, reads the title and description of that path from `src/data/pages.json`:

```json title="src/data/pages.json"
{
  "path": "/reports",
  "title": "Reports",
  "layout": "dashboard",
  "description": "Monthly sales and traffic reports."
}
```

The root layout, `src/app/layout.tsx`, has the title template `%s | AdminKit Demo`, so the page's title becomes `Reports | AdminKit Demo`. The home page (`/`) keeps the default title, `AdminKit Demo - Next.js Admin Template`. The title is in the static HTML of each page, so it is there before any JavaScript runs.

`pageMetadata` throws an error when the path is not in `pages.json`, so the build fails for a page without an entry. Keep the `title` in `pages.json` and the `title` of `PageTitle` the same, so the tab and the heading match.

`metadata` only works in a server component. So keep `page.tsx` a server component, and put the interactive parts in client components that it renders.

For screen reader users and the browser:

- `PageTitle` renders the page's only `<h1>`, styled as `.h3`. Use `<h2>` for card titles below it, as the demo pages do.
- Every page has its own document title from `metadata`, which screen readers announce and browser tabs and history show.
- The PRO badge opens a new tab (`target="_blank"`) without saying so. If you keep a link like it, add that to its text.

## Images and links

Use `next/link` for links between pages. Images are plain `<img>` elements with a `width` and `height`. Write their `src` with `asset()` from `src/lib/site.ts`, so they keep working under a [base path](/nextjs/docs/getting-started/deployment/#base-path):

```tsx
<img src={asset("/img/avatars/avatar.jpg")} width="40" height="40" alt="" />
```

## Imports

`@/` is `src/`: `@/components/bootstrap/modal` is `src/components/bootstrap/modal.tsx`.

---

Canonical HTML page: https://adminkit.io/nextjs/docs/getting-started/project-structure/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
