Skip to content
Orbix ASP.NET Core · Tailwind CSS v1.0.2

Adding a page and a menu item

Add a page

1. Create the Razor page src/Orbix/Pages/reports/sales.cshtml:

@page
@{
    Layout = "_Layout";
    ViewData["Title"] = "reports.sales.title";
    ViewData["Nav"] = "reports-sales";
}
<ox-page-header title="reports.sales.title" subtitle="reports.sales.subtitle" />

<div class="fd-page">
  <section class="fd-card">
    <div class="fd-card__body">
      <p data-i18n="reports.sales.empty">@L["reports.sales.empty"]</p>
    </div>
  </section>
</div>

L is the IStringLocalizer of the template (see Languages); data-i18n lets the in-page translator switch the text without a reload.

2. Register the URL in Infrastructure/Navigation/PageRoutes.cs (Razor page => URL):

["/reports/sales"] = "reports/sales",

Every page file must be listed (the tests check it). Links name the page, so the URL can change here without touching them: <a href="@Url.Page("/reports/sales")">.

3. Add the texts to every Locales/<code>.json file (see Languages):

"reports.sales.title": "Sales report",
"reports.sales.subtitle": "Revenue by channel and region",
"reports.sales.empty": "No sales in this period yet."

4. Optional page script. Create ClientApp/js/pages/reports.ts and set ViewData["Module"] = "reports":

// ClientApp/js/pages/reports.ts
export function init(): void {
  document.querySelectorAll<HTMLElement>('[data-report-refresh]').forEach((button) => {
    button.addEventListener('click', () => location.reload());
  });
}

The module is loaded only on pages that declare it, so each page downloads the code it needs. Run npm run build.

Visit http://localhost:5000/reports/sales.

Pages that need real data add a page model: @model SalesModel with a SalesModel : PageModel class next to the page (OnGetAsync() loads the data from your services), exactly as in any Razor Pages app.

Reusable blocks (partials)

The template's blocks live in Pages/Shared/Partials and are rendered with the <ox-partial> tag helper. Text arguments use the arg- prefix; markup goes in named <ox-slot> elements or in the body:

<ox-partial name="apps/mail-row" arg-from="Harbor & Tide" arg-subject="Q3 report" arg-preview="Numbers attached."
            arg-date="2026-10-18T09:40:00Z" arg-star="false" />

Inside the partial, Model["from"] reads an argument (encoded once by Razor), Model.Slot("badge") a named slot and Model.Body the body. Your own partials can use the standard <partial name="..." model="..." /> as well.

Add a menu item

The sidebar, the horizontal menu, the two-column rail and the command palette are all built from src/Orbix/menu.json. Sections hold items and groups; groups hold children:

{
  "id": "reports",
  "key": "nav.reports",
  "icon": "lc-chart-column",
  "items": [
    { "id": "reports-sales", "key": "nav.sales_report", "icon": "lc-chart-line", "page": "/reports/sales" },
    {
      "id": "reports-more",
      "key": "nav.more_reports",
      "icon": "lc-folder-open",
      "children": [
        { "id": "reports-stock", "key": "nav.stock_report", "page": "/reports/stock" }
      ]
    }
  ]
}
KeyMeaning
idUnique navigation id
pageRazor page of the entry; it is highlighted, and its group opens, when that page is rendered
keyTranslation key of the label
iconIcon id in the sprite: lc-* (Lucide), tb-* (Tabler), fd-i-* or ox-i-* (Orbix)
badgeOptional counter: { "count": 4, "tone": "primary" }

menu.json is read when the app starts: restart it (or let dotnet watch do it) after editing the file.

Icons

Use any icon of the bundled sets with an SVG <use> reference:

<svg class="fd-icon" aria-hidden="true"><use href="#lc-chart-line"/></svg>

Browse the sets on the Icons pages of the template. Only the icons you reference are included in the sprite: after adding a new lc-* or tb-* icon, run npm run assets (it also runs with npm run dev and npm run build).