ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Getting Started
Concepts
API Management
AI Gateway
MCP Gateway
MCP Server
Developer Portal
    Overview
    Getting Started
      Updating VersionsNode Modules & CustomizationZuplo Branding
      Configuration
        Colors & ThemeDocumentationNavigationBranding & LayoutSearchFooterllms.txt
      Writing
      OpenAPI
      Authentication
      Integrations
      Guides
      Extending
      Components
    Development
    Deploying & Source Control
    Analytics
    Observability
    Networking & Infrastructure
    Account Management
    Programming API
    Build with AI
    Zuplo CLI
    Migration Guides
    Platform LimitsVersion Support PolicySecuritySupportTrust & ComplianceChangelog
    powered by Zuplo
    Configuration

    Navigation

    Dev Portal uses a single navigation array to control both the top navigation tabs and the sidebar. Items at the root of this array appear as tabs, and nested items build the sidebar tree. Navigation entries can be links, document references, categories, custom pages, separators, sections, or filters.

    Basic configuration

    The navigation is defined using the navigation array in the Dev Portal config file. Each item can be one of several types. At the simplest level you may only have links and categories.

    Code
    { "navigation": [ { "type": "category", "label": "Documentation", "icon": "book", "items": [ { "type": "doc", "file": "documentation/introduction", "label": "Introduction", "icon": "file-text" }, { "type": "doc", "file": "documentation/getting-started", "path": "/docs/quick-start", "label": "Quick Start" } ] }, { "type": "link", "to": "/api", "label": "API Reference", "icon": "code", "badge": { "label": "v2.0", "color": "blue" }, "display": "always" } ] }

    Navigation Items

    Navigation items can be of these types: category, doc, link, custom-page, separator, section, or filter.

    • link: A direct link to a page or external URL.
    • category: A group of links that can be expanded or collapsed.
    • doc: A reference to a document by its file path: file.
    • custom-page: A custom page that is made of a React component, see Custom Pages
    • separator: A horizontal line to visually divide sidebar items.
    • section: A non-interactive heading label to group sidebar items.
    • filter: An inline search input that filters navigation items. Multiple filter inputs share the same search query.

    type: link

    link is the most basic item, it directly links to a path or URL. Use this for external resources or standalone pages.

    Code
    { "type": "link", "label": "Support", "to": "/my/api" // or: https://example.com/my-external-link }
    TypeScript type declaration
    Code
    type NavigationLink = { type: "link"; to: string; label: string; icon?: string; // Lucide icon name target?: "_self" | "_blank"; stack?: boolean; // open the destination as a stacked sub-nav badge?: { label: string; color: "green" | "blue" | "yellow" | "red" | "purple" | "indigo" | "gray" | "outline"; invert?: boolean; }; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    type: category

    The category type groups related items under a collapsible section. The label is the displayed text, and the items array can contain any navigation item type (documents, links, categories, custom pages, separators, sections, and filters).

    Code
    { "type": "category", "label": "Getting Started", "collapsible": true, // optional "collapsed": false, // optional "items": [ { "type": "link", "label": "Support", "to": "https://support.example.com" } ] }
    TypeScript type declaration
    Code
    type NavigationCategory = { type: "category"; icon?: string; // Lucide icon name items: Array<NavigationItem>; // any navigation item type, including string shorthands for docs label: string; collapsible?: boolean; collapsed?: boolean; stack?: boolean; // open the category's items as a stacked sub-nav link?: | string | { type: "doc"; file: string; label?: string; path?: string } | { type: "link"; to: string; label?: string }; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    Category links

    A category can have a link property that makes the category label itself clickable, navigating to a document, a path, or an external URL. This is useful when you want a category that acts as both a group and a landing page.

    The link can be a simple string pointing to a file path, a doc object for more control, or a link object that points the category label at an arbitrary path or external URL:

    Code
    { type: "category", label: "Configuration", link: "docs/configuration/overview", items: [ "docs/configuration/navigation", "docs/configuration/site", ], }
    Code
    { type: "category", label: "Documentation", link: { type: "doc", file: "home.md", path: "/", }, items: [ "guides/getting-started", "guides/advanced", ], }

    The doc object form supports these properties:

    PropertyTypeDescription
    type"doc"Must be "doc"
    filestringPath to the markdown file
    labelstringOverride the label (defaults to the document title)
    pathstringCustom URL path (overrides the default file-based route)
    Code
    { type: "category", label: "API Reference", link: { type: "link", to: "/api", }, items: [ "guides/authentication", "guides/rate-limits", ], }

    The link object form supports these properties:

    PropertyTypeDescription
    type"link"Must be "link"
    tostringPath or external URL to navigate to
    labelstringOverride the category label

    type: doc

    Doc is used to reference markdown files. The label is the text that will be displayed, and the file is the file path associated with a markdown file.

    Code
    { "type": "doc", "label": "Overview", "file": "docs/overview" }
    TypeScript type declaration
    Code
    type NavigationDoc = { type: "doc"; file: string; path?: string; icon?: string; label?: string; badge?: { label: string; color: "green" | "blue" | "yellow" | "red" | "purple" | "indigo" | "gray" | "outline"; invert?: boolean; }; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    Using shorthands

    Documents can be referenced as strings (using their file path), which is equivalent to { "type": "doc", "file": "path" }:

    Code
    { "navigation": [ { "type": "category", "label": "Documentation", "icon": "book", "items": [ "documentation/introduction", "documentation/getting-started", "documentation/installation" ] }, { "type": "link", "to": "/api", "label": "API Reference", "icon": "code" } ] }

    This is much more concise when you don't need custom labels, icons, or other properties for individual documents.

    Learn more in the Markdown documentation

    Custom paths

    The path property allows you to customize the URL path for a document. By default, Dev Portal uses the file path to generate the URL, but you can override this behavior by specifying a custom path.

    Code
    { type: "doc", file: "home.md", path: "/", label: "Home", }
    Code
    { type: "doc", file: "guides/getting-started.md", path: "/start-here", label: "Start Here", }

    When a file has a custom path, it will only be accessible at that custom path, not at its original file-based path. See Documentation - Custom Paths for more details.

    Avoid naming files index.md or index.mdx and relying on their default path. Zudoku's generated Vercel clean-URL routes strip /index with a redirect, and other hosting providers may do the same. This can cause routing issues. Instead, give files descriptive names and use the path property to serve them at the desired URL.

    type: custom-page

    Custom pages allow you to create standalone pages that are not tied to a Markdown document. This is useful for creating landing pages, dashboards, or any other custom content.

    Code
    { type: "custom-page", path: "/a-custom-page", element: <MyCustomPage />, display: "always" }
    TypeScript type declaration
    Code
    type NavigationCustomPage = { type: "custom-page"; path: string; label?: string; element: any; icon?: string; // Lucide icon name layout?: "default" | "none"; badge?: { label: string; color: "green" | "blue" | "yellow" | "red" | "purple" | "indigo" | "gray" | "outline"; invert?: boolean; }; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    Set layout: "none" to render the page without the default Dev Portal layout (header, sidebar, footer). This is useful for fully custom landing pages.

    type: separator

    A visual divider line in the sidebar. Use separators to create visual breaks between groups of items.

    Code
    { type: "category", label: "Documentation", items: [ "guides/getting-started", "guides/installation", { type: "separator" }, "guides/advanced", "guides/troubleshooting", ], }
    TypeScript type declaration
    Code
    type NavigationSeparator = { type: "separator"; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    type: section

    A non-interactive heading label in the sidebar. Sections are rendered as small uppercase text and are useful for labeling groups of items without adding a collapsible wrapper.

    Code
    { type: "category", label: "Documentation", items: [ { type: "section", label: "Getting Started" }, "guides/quickstart", "guides/installation", { type: "section", label: "Advanced" }, "guides/plugins", "guides/deployment", ], }
    TypeScript type declaration
    Code
    type NavigationSection = { type: "section"; label: string; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    type: filter

    An inline search input that updates the shared navigation filter. When the user types, the query is applied across the sidebar so that only matching items remain visible.

    Code
    { type: "category", label: "Documentation", items: [ { type: "filter", placeholder: "Filter documentation" }, "guides/getting-started", "guides/installation", "guides/authentication", "guides/deployment", ], }
    TypeScript type declaration
    Code
    type NavigationFilter = { type: "filter"; placeholder?: string; display?: | "auth" | "anon" | "always" | "hide" | ((params: { context: ZudokuContext; auth: UseAuthReturn }) => boolean); };

    Stacked navigation

    By default a category expands inline when clicked. Set stack: true and it opens as a full panel that slides in over the current menu, with a Back button to return. Use it for deep sections that would otherwise crowd the sidebar.

    stack works on categories and links. The panel content comes from the item itself:

    • A category with stack: true shows its own items.
    • A link with stack: true opens the navigation at its destination, such as the sidebar a plugin generates at that path.
    Code
    { type: "category", label: "Shipping Guides", stack: true, items: ["guides/interstellar", "guides/intergalactic"], }
    Code
    { type: "link", label: "Developer API", to: "/graphql/dev", stack: true, }

    The Back button points to the section the stacked item belongs to (for example "Back to Our APIs"), so the page still works when someone opens it directly from a search result or a shared link.

    A stacked link drills into wherever its to points, so the destination should be a section with its own navigation, such as a plugin route or a category landing page. The slide animation respects the user's reduced-motion setting and falls back to an instant swap.

    Stacking generated navigation

    Plugin-generated sidebars (e.g. OpenAPI) can be stacked too, with a navigation rule. This is useful for large APIs: instead of one long scroll, make each tag drill into its own panel.

    Code
    navigationRules: [ { type: "modify", match: "Shipments/Shipment/Shipment Management", set: { stack: true }, }, ],

    The match targets a generated category by label (here, the Shipment Management tag under the Shipments API). See Navigation Rules for the matching syntax.

    Display Control

    All navigation items support a display property that controls when the item should be visible:

    • "always" (default): Always visible
    • "auth": Only visible when user is authenticated
    • "anon": Only visible when user is not authenticated
    • "hide": Never visible (useful for temporarily hiding items)
    • callback function: For custom logic, pass a function that receives { context, auth } and returns a boolean
    Code
    { "type": "link", "label": "Admin Panel", "to": "/admin", "display": "auth" }

    Custom Display Logic

    For more complex visibility rules, use a callback function:

    Code
    { type: "link", label: "Premium Features", to: "/premium", display: ({ auth }) => { // Show only for users with premium role return auth.user?.role === "premium"; } }

    The callback receives:

    • context: The ZudokuContext instance
    • auth: The authentication state from UseAuthReturn, including user, isAuthenticated, etc.

    Badges

    Navigation items can display badges with labels and colors. Badges support an optional invert property for styling:

    Code
    { "type": "doc", "file": "api/v2", "badge": { "label": "Beta", "color": "yellow" } }

    Icons

    Icons can be added to categories and documents by specifying an icon property. The value should be the name of a Lucide icon (e.g., book , code , file-text ).

    Code
    { "type": "category", "label": "Getting Started", "icon": "book" }

    They can also be set on individual documents in their front matter:

    Code
    --- title: My Document sidebar_icon: book ---

    Title & Labels

    All navigation items can have a label property that determines the displayed text. For doc items, the label is optional; if omitted, Dev Portal uses the document's title from its front matter or the first # header.

    To override the navigation label without changing the document's title, use the sidebar_label property in the front matter:

    Code
    --- title: My Long Title sidebar_label: Short Title ---

    In this example, the document's title remains "My Long Title," but the sidebar displays "Short Title."

    For the complete list of supported frontmatter properties, see Frontmatter.

    Common Patterns

    Serving a document at the root URL

    To make a markdown document accessible at /, use the path property to override the default file-based route:

    Code
    navigation: [ { type: "doc", file: "home.md", path: "/", label: "Home", }, ],
    Code
    navigation: [ { type: "category", label: "Documentation", link: { type: "doc", file: "home.md", path: "/", }, items: [ "guides/getting-started", "guides/installation", ], }, ],

    Landing page with hidden tab

    Use a custom-page with display: "hide" and layout: "none" to create a full-page landing experience that doesn't appear in the navigation tabs:

    Code
    navigation: [ { type: "custom-page", path: "/", display: "hide", layout: "none", element: <LandingPage />, }, { type: "category", label: "Documentation", items: ["docs/quickstart", "docs/installation"], }, ],

    Organized sidebar with sections and separators

    Combine section, separator, and filter items to create a well-structured sidebar:

    Code
    navigation: [ { type: "category", label: "Documentation", items: [ { type: "filter", placeholder: "Filter documentation" }, { type: "section", label: "Getting Started" }, "guides/quickstart", "guides/installation", { type: "separator" }, { type: "section", label: "Advanced" }, { type: "category", label: "Plugins", icon: "blocks", items: ["plugins/overview", "plugins/custom"], }, ], }, ],

    Navigation Rules

    Plugins generate sidebar navigation automatically (e.g. from OpenAPI tags). Navigation rules let you customize that generated sidebar by inserting, modifying, sorting, moving, or removing items. To change the top-level tabs themselves, use the navigation array directly.

    Code
    navigationRules: [ { type: "sort", match: "Shipments", by: (a, b) => a.label.localeCompare(b.label), }, ],

    For a practical walkthrough with more examples, see the Navigation Rules guide.

    Rule Types

    Each rule has a type and a match property that targets a navigation item.

    TypeDescription
    insertAdd items before or after a matched item
    modifyChange label, icon, badge, collapsed, collapsible, display, or stack
    removeRemove a matched item from the sidebar
    sortSort children of a matched category using a custom comparator
    moveRelocate a matched item before or after another item

    Match Syntax

    The match property uses slash-separated segments to target items. The first segment identifies the top-level tab by label, and remaining segments navigate into the sidebar tree. Label matching is case-insensitive.

    Code
    match: "Users/Get User"; // by label match: "Users/0"; // first item match: "Users/-1"; // last item match: "Users/Advanced/0"; // mixed nesting
    Edit this page
    Last modified on October 1, 2026
    DocumentationBranding & Layout
    On this page
    • Basic configuration
    • Navigation Items
      • type: link
      • type: category
      • type: doc
      • type: custom-page
      • type: separator
      • type: section
      • type: filter
    • Stacked navigation
      • Stacking generated navigation
    • Display Control
      • Custom Display Logic
    • Badges
    • Icons
    • Title & Labels
    • Common Patterns
      • Serving a document at the root URL
      • Landing page with hidden tab
      • Organized sidebar with sections and separators
    • Navigation Rules
      • Rule Types
      • Match Syntax
    React
    JSON
    TypeScript
    JSON
    TypeScript
    React
    React
    React
    JSON
    TypeScript
    JSON
    React
    React
    React
    TypeScript
    React
    TypeScript
    React
    TypeScript
    React
    TypeScript
    React
    React
    React
    JSON
    React
    JSON
    JSON
    Markdown
    Markdown
    React
    React
    React
    React
    React
    JSON