***

title: 'Creating Pages'
metaTitle: "Creating Custom Dashboard Pages in Vendure"
metaDescription: "Build custom pages for the Vendure Dashboard using Page, PageLayout, and PageBlock components, and register them with routes and navigation items."
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Page Structure

All pages in the Dashboard follow this structure:

```tsx title="src/plugins/example/dashboard/test-page.tsx"
import { Page, PageBlock, PageLayout, PageTitle } from '@vendure/dashboard';

export function TestPage() {
    return (
        <Page pageId="test-page">
            <PageTitle>Test Page</PageTitle>
            <PageLayout>
                <PageBlock column="main" blockId="main-stuff">
                    This will display in the main area
                </PageBlock>
                <PageBlock column="side" blockId="side-stuff">
                    This will display in the side area
                </PageBlock>
            </PageLayout>
        </Page>
    )
}
```

* [Page component](/current/core/reference/dashboard/page-layout/page)
  * [PageTitle component](/current/core/reference/dashboard/page-layout/page-title)
  * [PageLayout component](/current/core/reference/dashboard/page-layout)
    * [PageBlock components](/current/core/reference/dashboard/page-layout/page-block)

Following this structure ensures that:

* Your pages look consistent with the rest of the Dashboard
* Your page content is responsive
* Your page can be further extended using the [pageBlocks API](/current/core/extending-the-dashboard/customizing-pages/page-blocks)

:::info
Note that the [ListPage](/current/core/reference/dashboard/list-views/list-page) and [DetailPage](/current/core/reference/dashboard/detail-views/detail-page)
components internally use this same structure, so when using those top-level components you don't need to wrap them
in `Page` etc.
:::

## Block Columns

The `PageLayout` arranges its `PageBlock` children into columns based on each block's `column` prop:

* **`main`**: the wider main content area
* **`side`**: the narrower sidebar
* **`full`**: spans the full width of the layout, above the main/side columns

```tsx title="src/plugins/example/dashboard/test-page.tsx"
import { Page, PageBlock, PageLayout, PageTitle } from '@vendure/dashboard';

export function TestPage() {
    return (
        <Page pageId="test-page">
            <PageTitle>Test Page</PageTitle>
            <PageLayout>
                <PageBlock column="full" blockId="full-stuff">
                    This block spans the full width
                </PageBlock>
                <PageBlock column="main" blockId="main-stuff">
                    This will display in the main area
                </PageBlock>
                <PageBlock column="side" blockId="side-stuff">
                    This will display in the side area
                </PageBlock>
            </PageLayout>
        </Page>
    );
}
```

:::info
Full-width blocks are always rendered **above** the `main`/`side` columns, regardless of their
order among the `PageLayout` children. This makes `column="full"` well-suited to wide content such
as data tables on list pages.
:::

### Full-width without a card

A `PageBlock` wraps its content in a bordered card. If you want a full-width block with **no** card
chrome — for example, a block that supplies its own container such as a `DataTable` — use the
[FullWidthPageBlock](/current/core/reference/dashboard/page-layout/page-block) component instead:

```tsx
import { FullWidthPageBlock, Page, PageLayout, PageTitle } from '@vendure/dashboard';

<PageLayout>
    <FullWidthPageBlock blockId="list-table">
        <DataTable /* ... */ />
    </FullWidthPageBlock>
</PageLayout>
```

## Page Routes & Navigation

Once you have defined a page component, you'll need to make it accessible to users with:

* A route (url) by which it can be accessed
* Usually a navigation bar entry in the main side navigation of the Dashboard

Both of these are handled using the [DashboardRouteDefinition API](/current/core/reference/dashboard/extensions-api/routes):

```tsx title="src/plugins/example/dashboard/index.tsx"
import { defineDashboardExtension } from '@vendure/dashboard';

import { TestPage } from './test-page';

defineDashboardExtension({
    routes: [
        {
            // The TestPage will be available at e.g. 
            // http://localhost:5173/dashboard/test
            path: '/test',
            // The loader function is allows us to define breadcrumbs
            loader: () => ({ breadcrumb: 'Test Page' }),
            // Here we define the nav menu items
            navMenuItem: {
                // a unique ID
                id: 'test',
                // the nav menu item label
                title: 'Test Page',
                // which section it should appear in
                sectionId: 'catalog',
            },
            component: TestPage,
        },
    ],
});
```

:::info
For a complete guide to the navigation options available, see the [Navigation guide](/current/core/extending-the-dashboard/navigation/)
:::
