Dyrected
Editor ExperienceCustom Components

Overview

Swap your own React (or Vue) components into the admin at two extension points — field inputs and dashboard/list slots — and know where the boundaries are.

The admin is generated from your schema, but you're not stuck with the built-in look of every piece. Dyrected gives you two clean extension points: you can replace a field's input with your own component, and you can inject components into slots on the dashboard and collection lists. This page is the map — it explains both mechanisms, shows the registration pattern they share, and is honest about what you can't customize this way.

Throughout, "your app" means the project you embedded the admin into — your Next.js, Nuxt, Vue, or React application.

The mental model

Every custom component follows the same two-step pattern: declare where it goes, then provide the component.

  • You reference a component by a string key — either on a field (admin.component) or in a slot list (admin.components) in your schema.
  • You pass the actual component under that same key in the components prop of <DyrectedAdmin>.

There's no import map, no build step, and no string module paths. You hand real component references to the admin, and it renders them where your config said to. If a key has no matching component, the admin skips it (and warns in development); if a custom component throws, an error boundary isolates it so the rest of the admin keeps working.

There are exactly two extension points:

  1. Custom field components — replace the input for a specific field.
  2. Component slots — inject components into fixed positions on the dashboard and collection lists.

Custom field components

When a field needs an input the built-in set doesn't cover — a color picker, a map, a code editor — point the field at your own component. Set admin.component to a key of your choosing on the field:

export const Products = defineCollection({
  slug: "products",
  fields: [
    {
      name: "brandColor",
      label: "Brand color",
      type: "text",
      admin: {
        component: "brand-color-picker",
        description: "Brand hex color, e.g. #6366f1",
      },
    },
  ],
});

Then register the component under the same key in the components.fields map when you mount the admin:

import { DyrectedAdmin } from "@dyrected/next/admin";
import { BrandColorPicker } from "./BrandColorPicker";

export default function AdminPage() {
  return (
    <DyrectedAdmin
      components={{
        fields: { "brand-color-picker": BrandColorPicker },
      }}
    />
  );
}

The key is yours to name — it's matched by the admin.component string, not by field type. That means a custom component applies to the specific field (or fields) that opt into it, not to every field of a given type.

What your component receives

Your field component is handed everything it needs to read and write the value, plus context about where it's rendering:

PropWhat it is
valueThe current field value
onChangeCall it with the new value to update the form
fieldThe field's schema definition from your config
pathThe field's name
disabledtrue when the field is read-only or the user lacks update access
collectionThe slug of the collection the field belongs to
context{ user, schemas, siblingData } — the current user, all loaded schemas, and the sibling field values in the same document

Read from value, call onChange to update, and use context.siblingData if your input depends on other fields. Because the admin resolves access before rendering, honoring disabled is usually all you need for permissions.

Component slots

Slots are fixed positions where you can add components without rebuilding a whole screen — a banner above the dashboard, a header above a collection list, a widget after a table. They use the same declare-then-provide pattern, but you declare them in an admin.components object rather than on a field.

Declare the slot keys in config — on defineConfig for the dashboard, or on a collection for its list:

export const Posts = defineCollection({
  slug: "posts",
  admin: {
    components: {
      beforeList: ["posts-header"],
    },
  },
  fields: [...],
});

export default defineConfig({
  collections: [Posts],
  admin: {
    components: {
      beforeDashboard: ["welcome-banner"],
    },
  },
});

Then register the components, grouped by where they live:

<DyrectedAdmin
  components={{
    dashboard: { "welcome-banner": WelcomeBanner },
    collectionList: { "posts-header": PostsHeader },
  }}
/>

Each slot list is an array, so you can stack more than one component in a slot. The two slot groups get different props suited to where they render — the dashboard slots receive the client, user, and schemas; the list slots also receive the collection, its documents, pagination, and permissions. Each group has its own page:

  • Dashboard — the beforeDashboard / afterDashboard slots and their props.
  • List View — the beforeList / beforeListTable / afterListTable / afterList slots and their props.

Using Vue components

If your app is Vue or Nuxt, you register Vue components exactly the same way — pass them in the components prop. The Vue integration wraps them automatically so they render inside the React-based admin; you don't do anything special beyond passing them. The props described above arrive as Vue props.

What you can't customize this way

It's worth being clear about the edges so you don't go looking for something that isn't there. These two extension points — field components and dashboard/list slots — are the whole surface today. Dyrected does not currently support:

  • Replacing root-level UI (the navigation, header, login screen, or logo as a component — the logo is set with branding config, not a component).
  • Injecting custom React context providers around the admin.
  • Replacing or adding whole views (a custom edit view, document tabs, custom routes, or a full list-view replacement).

If your goal is theming rather than swapping components, that's Customizing CSS. If you need one of the unsupported customizations above, it's a genuine gap — raise it rather than working around it.

Reach for a custom field component when a specific field needs an input the defaults don't offer, and a slot when you want to add something alongside the built-in dashboard or list without rebuilding it. Start from this page's pattern — declare a key, register the component — then go to the Dashboard or List View page for the exact props your slot receives.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend