Admin UI
How the Dyrected admin works, where it can live, and what changes when you embed 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.
There are two ways editors can use it:
- Hosted admin at
app.dyrected.com, managed by Dyrected - Embedded admin inside your own app, usually at
/admin
That choice is separate from the backend choice. You can use the admin with either a Dyrected Cloud backend or a self-hosted backend.
Embedding the Admin
The Admin UI is a React application exported from @dyrected/admin. Every framework integration is a thin wrapper around one of two primitives:
| Primitive | When to use |
|---|---|
<AdminUI /> | You are already in a React tree (Next.js, Vite + React, CRA) |
renderAdminUI(el, props) | You are outside React (Vue, Nuxt, Svelte, vanilla JS) |
Next.js
@dyrected/next wraps <AdminUI /> with server-component guards. Create a catch-all route:
// app/admin/[[...path]]/page.tsx
import { DyrectedAdmin } from "@dyrected/next/admin";
export default function AdminPage() {
return <DyrectedAdmin />;
}React (Vite / CRA)
Use <AdminUI /> directly from @dyrected/admin:
// src/pages/Admin.tsx
import { AdminUI } from "@dyrected/admin";
export function AdminPage() {
return (
<div style={{ height: "100vh" }}>
<AdminUI baseUrl={import.meta.env.VITE_DYRECTED_URL} apiKey={import.meta.env.VITE_DYRECTED_API_KEY} />
</div>
);
}Vue 3
@dyrected/vue ships a <DyrectedAdmin /> component that calls renderAdminUI internally. The Admin UI must be client-side only — wrap it in a conditional or use your framework's client-only mechanism:
<!-- src/pages/Admin.vue -->
<script setup lang="ts">
import { DyrectedAdmin } from "@dyrected/vue";
</script>
<template>
<DyrectedAdmin base-url="https://api.dyrected.cloud" api-key="sk_live_..." />
</template>Nuxt
@dyrected/nuxt auto-imports <DyrectedAdmin /> globally. Always render it inside <ClientOnly> since the Admin UI requires the browser DOM:
<!-- pages/admin.vue -->
<script setup lang="ts">
definePageMeta({ layout: false });
</script>
<template>
<ClientOnly>
<DyrectedAdmin />
</ClientOnly>
</template>Svelte / Vanilla JS — renderAdminUI
renderAdminUI(container, props) mounts the Admin UI into any DOM element and returns an unmount function. Use it in any non-React environment:
import { renderAdminUI } from "@dyrected/admin";
const container = document.getElementById("admin-root")!;
const unmount = renderAdminUI(container, {
baseUrl: "https://api.dyrected.cloud",
apiKey: "sk_live_...",
});
// To tear down (e.g. on SPA route leave):
// unmount()Svelte example:
<script>
import { onMount, onDestroy } from 'svelte'
import { renderAdminUI } from '@dyrected/admin'
let container
let unmount
onMount(() => {
unmount = renderAdminUI(container, {
baseUrl: import.meta.env.VITE_DYRECTED_URL,
apiKey: import.meta.env.VITE_DYRECTED_API_KEY,
})
})
onDestroy(() => unmount?.())
</script>
<div bind:this={container} style="height: 100vh" />AdminUI / renderAdminUI Props
Both accept the same props:
Prop
Type
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. It is designed as a lightweight editorial workspace rather than an analytics screen.
- Quick actions: A compact
Newdropdown for creating documents in editable collections, plus direct actions for upload collections and the first visible global. - Recent edits: Shows recently updated documents from editable collections, using
admin.useAsTitlewhen available. - Needs attention: Highlights simple setup and content-model issues, such as missing
admin.useAsTitleor missing media alt text fields. - Setup state: Shows a setup prompt if no collections or globals have been configured 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). - Title column: The column for the
admin.useAsTitlefield (or the first visible field) is rendered as a clickable link that navigates directly to the edit page. - Columns: Initial columns are controlled by
admin.defaultColumns. Falls back to a sensible set of visible fields and system timestamps.row,join, and unknown column names are ignored automatically. - View preferences: Editors reorder or toggle columns using the View button. On the edit page, the same button switches to a drag-to-reorder mode for fields. Preferences are saved personally per editor; admins can also publish a team-wide default using Save for Everyone. See View Preferences.
- Filters: The filter builder uses a draft/apply flow, so editors can add and edit filter rules before applying them to the list query.
- Status column: Automatically added if the collection has a
statusfield. - Actions: Edit (navigates to edit page), 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
statusfield. - Live Preview: If
admin.previewUrlis 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
- Responsive detail dialog with scrollable metadata and image preview
- Form-level media pickers use the same grid selection language as the Media Library
- 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.
Sidebar Structure
The sidebar is generated from your config at runtime:
- Grouped collections — Collections sharing the same
admin.groupvalue are nested under a labelled, collapsible section. - Ungrouped collections — Appear at the top level in definition order.
- Upload collections — Grouped into a "Media" section automatically.
- Globals — Listed below collections, also respecting
admin.group. - Hidden items — Collections or globals with
admin.hidden: trueare excluded.
Collections and globals can set admin.icon to a valid Lucide component name such as Newspaper, ShoppingBag, or Palette. TypeScript autocompletes supported names. A configured icon overrides the contextual default used for upload, auth, regular collection, and global navigation items.
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:
Prop
Type
Field Type → Admin Component Mapping
| Field Type | Admin Component | Notes |
|---|---|---|
text | Text input | |
textarea | Textarea | Auto-expanding |
richText | Tiptap editor | Block-based, floating toolbar |
number | Number input | |
boolean | Checkbox | Use admin.layout: 'switch' for switch rendering |
date | Calendar picker | |
select | Dropdown | Supports admin.layout: 'radio' for radio button rendering |
multiSelect | Tag-based multi-select | |
email | Email input | Client-side format validation |
url | URL input | With "Open in new tab" button |
icon | Icon picker popover | Searchable grid of all Lucide icons; stores icon name as string |
relationship | Searchable combobox | Thumbnail grid for upload collections |
join | Read-only related-docs list | Virtual field — no data stored; shows docs from another collection that reference this one |
array | Expandable card list | Drag-to-reorder, duplicate, move, and delete |
object | Grouped field panel | Collapsible |
json | Code editor | JSON syntax highlighting |
blocks | Block picker + inline editor | One editor per block type |
row | Flex row container | Virtual layout field — arranges children side-by-side; no data stored |