Dyrected
Admin UI

Admin UI

How the Dyrected Admin dashboard works, how to embed it, and how to customise it.

The @dyrected/admin package is a React-based Admin UI that is automatically generated from your dyrected.config.ts. You define your schema once — the Admin renders the forms, tables, media browser, and global editors without any additional code.


Embedding the Admin

In Next.js

Create a catch-all page under your chosen admin path:

import { AdminUI } from '@dyrected/admin'
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
import '@dyrected/admin/dist/index.css'

export default function AdminPage() {
  const router = useRouter()

  return (
    <AdminUI
      baseUrl={process.env.NEXT_PUBLIC_CMS_URL!}
      apiKey={process.env.NEXT_PUBLIC_CMS_API_KEY!}
      siteId={process.env.NEXT_PUBLIC_SITE_ID}
      basename="/admin"
      onNavigate={(path) => router.push(`/admin${path}`)}
    />
  )
}

In Nuxt

The @dyrected/nuxt module provides a <DyrectedAdmin /> component for easy embedding. Create a catch-all page or a dedicated admin route:

<!-- pages/cms-admin.vue -->
<script setup lang="ts">
// Disable Nuxt layout for the dashboard
definePageMeta({ layout: false })
</script>

<template>
  <ClientOnly>
    <DyrectedAdmin basename="/cms-admin" />
  </ClientOnly>
</template>

Standalone (self-hosted)

When running the @dyrected/core app directly, the Admin UI is served at /admin automatically. No separate setup is needed.


AdminUI Props

PropTypeRequiredDescription
baseUrlstringThe base URL of your Dyrected API (e.g. https://cms.mysite.com).
apiKeystringThe Site API key used to authenticate all Admin requests.
siteIdstringThe Site ID sent as X-Site-Id on every request (required in Cloud mode).
basenamestringThe base path where the admin is mounted (default: /admin).
onNavigatefunctionCallback fired on internal route change. Receives the path (relative to basename).

Standalone Components

The admin library also exports specialized components for targeted use cases.

SetupPromptUI

The AI integration prompt can be embedded standalone in your own application if you want to guide users through schema setup without mounting the full dashboard.

import { SetupPromptUI } from '@dyrected/admin'

<SetupPromptUI config={{ baseUrl, apiKey }} />

Admin UI Pages

Dashboard

The landing page. Shows a count of all collections and globals, quick links to recent collections, and a setup prompt if no schema has been synced yet.

Collection List

A sortable, searchable data table for any collection.

  • Search: The search input queries the field specified by admin.useAsTitle (or the first visible field).
  • Columns: Controlled by admin.defaultColumns. Falls back to the first three non-hidden fields.
  • Status column: Automatically added if the collection has a status field.
  • Actions: Edit (navigates to edit page), Copy ID, Delete.

Collection Edit / Create

The main content editor. Fields are rendered in the order they appear in your fields array.

  • Sidebar: Shows document ID, created/updated timestamps, and a publishing status panel if the collection has a status field.
  • Live Preview: If admin.previewUrl is set, a split-pane iframe renders alongside the form.
  • Unsaved changes: The browser prompts before leaving if there are unsaved edits.

Media Page

Shown automatically for any collection with upload: true.

  • Grid of uploaded files with thumbnails (images) or file-type icons (documents, videos, etc.)
  • Click to view file details: URL, dimensions, file size, alt text
  • Drag-and-drop upload zone
  • Delete with confirmation

Global Editor

A single full-page form for editing a Global's fields. Identical to the collection edit form but without list navigation.


The sidebar is generated from your config at runtime:

  1. Grouped collections — Collections sharing the same admin.group value are nested under a labelled, collapsible section.
  2. Ungrouped collections — Appear at the top level in definition order.
  3. Upload collections — Grouped into a "Media" section automatically.
  4. Globals — Listed below collections, also respecting admin.group.
  5. Hidden items — Collections or globals with admin.hidden: true are excluded.

Customising Branding

Pass a top-level admin key in your defineConfig to override default styling:

export default defineConfig({
  admin: {
    branding: {
      logo: '/logo.svg',
      logoMark: '/logomark.svg',
      primaryColor: '#6366f1',
      favicon: '/favicon.ico',
    },
    meta: {
      titleSuffix: '— Acme CMS',
    },
  },
  collections: [...],
})

See Admin Config Reference for the full property list.


Custom Field Components

You can replace the default Admin UI widget for any field type or specific field with your own React component:

import { AdminUI } from '@dyrected/admin'
import { MyMapPicker } from './components/MyMapPicker'
import { MyColorPicker } from './components/MyColorPicker'

<AdminUI
  baseUrl="..."
  apiKey="..."
  components={{
    fields: {
      // Replace ALL fields of type 'json' with your component
      json: MyCodeEditor,
      // Or target a specific field by name (takes precedence over type)
      'products.locationCoords': MyMapPicker,
      'settings.brandColor': MyColorPicker,
    }
  }}
/>

Custom Component Props

Your component receives:

PropTypeDescription
valueanyThe current field value.
onChange(value: any) => voidCallback to update the value.
fieldFieldSchemaThe full field definition from your config.
readOnlybooleanWhether the field should be non-editable.

Field Type → Admin Component Mapping

Field TypeAdmin ComponentNotes
textText input
textareaTextareaAuto-expanding
richTextTiptap editorBlock-based, floating toolbar
numberNumber input
booleanToggle switch
dateCalendar picker
selectDropdown
multiSelectTag-based multi-select
emailEmail inputClient-side format validation
urlURL inputWith "Open in new tab" button
relationshipSearchable comboboxThumbnail grid for upload collections
arrayRepeatable card listDrag-to-reorder
objectGrouped field panelCollapsible
jsonCode editorJSON syntax highlighting
blocksBlock picker + inline editorOne editor per block type

On this page