技能 编程开发 管理后台UI扩展与定制

管理后台UI扩展与定制

v20260825
webiny-admin-ui-extensions
用于深度定制Webiny的后台管理界面。支持进行品牌化改造(包括Logo、标题和主题色),为内容列表添加自定义数据列,并为网站构建器中的页面类型创建专属表单。能够扩展对话框、抽屉和富文本编辑器,实现高度个性化的后台功能。
获取技能
399 次下载
概览

Admin UI Extensions

TL;DR

Admin extensions customize the Webiny Admin application. There are three main categories: white-labeling (logos, titles, theme colors), custom data list columns (adding columns to content entry tables), and custom page-type forms (custom form fields for Website Builder page types). All are React components registered via <Admin.Extension> in webiny.config.tsx.

YOU MUST include the full file path with the .tsx extension in every src prop. For example, use src={"/extensions/MyAdminExtension.tsx"}, NOT src={"/extensions/MyAdminExtension"}. Omitting the file extension will cause a build failure.

White-Labeling

Theme Colors

// extensions/AdminBranding/AdminTheme.tsx
import React from "react";
import { AdminConfig } from "webiny/admin/configs";

const { Theme } = AdminConfig;

const AdminTheme = () => {
  return (
    <AdminConfig.Public>
      <Theme.Color palette={"primary"} color={"purple"} />
      <Theme.Color palette={"secondary"} color={"green"} />
    </AdminConfig.Public>
  );
};

export default AdminTheme;
  • palette -- "primary", "secondary", "neutral", etc.
  • color -- any CSS color value: named colors, hex ("#6B46C1"), or RGB.

Logo and Title

// extensions/AdminBranding/AdminTitleLogo.tsx
import React from "react";
import { AdminConfig } from "webiny/admin/configs";
import squareLogo from "./logo.png";
import horizontalLogo from "./logo.png";

const { Title, Logo } = AdminConfig;

const AdminTitleLogo = () => {
  return (
    <AdminConfig.Public>
      <Title value={"ACME Corp"} />
      <Logo
        squareLogo={<img src={squareLogo} alt={"ACME Corp"} />}
        horizontalLogo={<img src={horizontalLogo} alt={"ACME Corp"} />}
      />
    </AdminConfig.Public>
  );
};

export default AdminTitleLogo;

Register both:

<Admin.Extension src={"/extensions/AdminBranding/AdminTheme.tsx"} />
<Admin.Extension src={"/extensions/AdminBranding/AdminTitleLogo.tsx"} />

Available AdminConfig Components

Component Purpose
<Theme.Color palette="..." color="..." /> Set theme color palette
<Title value="..." /> Set the Admin app title
<Logo squareLogo={...} horizontalLogo={...} /> Set square and horizontal logos

All must be wrapped in <AdminConfig.Public>.

Custom Data List Columns

Add custom columns to the content entry list view in the Admin UI. Columns can be restricted to specific content models.

Full Example: Email Columns for Contact Submissions

// extensions/contactSubmission/EmailEntryListColumn.tsx
import React from "react";
import { ContentEntryListConfig } from "webiny/admin/cms/entry/list";

const { Browser } = ContentEntryListConfig;

// Custom cell component for the Email Type column
interface ContactSubmissionTableRow {
  values: {
    emailType: "work" | "personal";
  };
}

export const EmailTypeCell = () => {
  const { useTableRow, isFolderRow } = ContentEntryListConfig.Browser.Table.Column;
  const { row } = useTableRow<ContactSubmissionTableRow>();

  if (isFolderRow(row)) {
    return <>{"-"}</>;
  }

  const emailType = row.data.values.emailType;
  return emailType === "work" ? <>{"Business"}</> : <>{"Personal"}</>;
};

// Main extension component
const EmailEntryListColumn = () => {
  return (
    <ContentEntryListConfig>
      {/* Simple column using path (no custom cell needed) */}
      <Browser.Table.Column
        name={"email"}
        after={"name"}
        path={"values.email"}
        header={"Email"}
        modelIds={["contactSubmission"]}
      />
      {/* Custom cell column */}
      <Browser.Table.Column
        name={"emailType"}
        after={"email"}
        header={"Email Type"}
        modelIds={["contactSubmission"]}
        cell={<EmailTypeCell />}
      />
    </ContentEntryListConfig>
  );
};

export default EmailEntryListColumn;

Register:

<Admin.Extension src={"/extensions/contactSubmission/EmailEntryListColumn.tsx"} />

Column Props Reference

Prop Type Description
name string Unique column identifier
header string Column header text
path string Dot-path to the data field (e.g., "values.email") -- for simple columns
cell ReactElement Custom React component for complex rendering
modelIds string[] Restrict column to specific content models
after string Position this column after another column by name

Custom Cell Hooks

Inside a custom cell component:

  • useTableRow<T>() -- access the full row data, typed with your interface
  • isFolderRow(row) -- check if the current row is a folder (return placeholder content)

Custom Page-Type Forms

Create custom forms for Website Builder page types using Webiny's form components:

// extensions/customPageTypes/RetailPageForm.tsx
import React from "react";
import { PageListConfig } from "webiny/admin/website-builder/page/list";
import { Bind, UnsetOnUnmount, validation } from "webiny/admin/form";

const { PageType } = PageListConfig;

export const RetailPageForm = () => {
  const form = useForm();

  return (
    <>
      {/* Mount the default page form fields. */}
      <PageType.Language />
      <PageType.Title />
      <PageType.Path />
      {/* Add custom fields.*/}
      <Grid.Column span={12}>
        <UnsetOnUnmount name={"extensions.customField"}>
          <Bind name={"extensions.customField"} validators={[validation.create("required")]}>
            <Input label={"Custom Field"} />
          </Bind>
        </UnsetOnUnmount>
      </Grid.Column>
    </>
  );
};

Form Components Reference

Component / Hook Import Purpose
Bind "webiny/admin/form" Bind a form field to a name path
useForm() "webiny/admin/form" Access the form API (getValue, setValue)
validation "webiny/admin/form" Create validators (validation.create("required"))
UnsetOnUnmount "webiny/admin/form" Clear the field value when the component unmounts
Grid.Column "webiny/admin/ui" Layout grid column (span={12} for full width)
Input "webiny/admin/ui" Text input field
Select "webiny/admin/ui" Dropdown select with options
FormApi "webiny/admin/form" Type for the form API object

Named Dialogs

Register a dialog by name, open it from anywhere with params, and consume params inside the dialog.

Register a Named Dialog

// extensions/MyDialog.tsx
import React from "react";
import { AdminConfig } from "webiny/admin/configs";
import { useDialog } from "webiny/admin";
import { Dialog } from "webiny/admin/ui";

const MyDialog = () => {
  const { params, closeDialog } = useDialog();

  return (
    <Dialog open onClose={closeDialog} title="My Dialog">
      <p>Received param: {params.itemId as string}</p>
      <Dialog.CancelAction onClick={closeDialog} text="Close" />
    </Dialog>
  );
};

const MyDialogConfig = () => {
  return (
    <AdminConfig>
      <AdminConfig.Dialog name="my-dialog" element={<MyDialog />} />
    </AdminConfig>
  );
};

export default MyDialogConfig;

Open a Named Dialog

import { useOpenDialog } from "webiny/admin";

const MyButton = () => {
  const { openDialog } = useOpenDialog();

  return <button onClick={() => openDialog("my-dialog", { itemId: "abc" })}>Open Dialog</button>;
};

Typed Params with Zod

import { z } from "zod";
import { useDialog } from "webiny/admin";

const paramsSchema = z.object({ itemId: z.string() });

const MyDialog = () => {
  const { params, closeDialog } = useDialog(paramsSchema);
  // params.itemId is typed as string
};

Dialog API Reference

Hook / Component Import Purpose
AdminConfig.Dialog "webiny/admin/configs" Register a named dialog
useDialog(schema?) "webiny/admin" Read params inside a named dialog
useOpenDialog(schema?) "webiny/admin" Open a named dialog with params
useDialogs() "webiny/admin" Low-level access to openNamedDialog / closeNamedDialog

Only one named dialog can be open at a time. Opening a new one replaces the current one.

Named Drawers

Register a drawer (slide-in panel) by name, open it from anywhere with params. Multiple drawers can be open simultaneously — they stack on top of each other.

Register a Named Drawer

// extensions/MyDrawer.tsx
import React from "react";
import { AdminConfig } from "webiny/admin/configs";
import { useDrawer } from "webiny/admin";
import { Drawer } from "webiny/admin/ui";

const MyDrawer = () => {
  const { params, closeDrawer } = useDrawer();

  return (
    <Drawer open onOpenChange={open => !open && closeDrawer()} title="My Drawer" size="md">
      <p>Item: {params.itemId as string}</p>
    </Drawer>
  );
};

const MyDrawerConfig = () => {
  return (
    <AdminConfig>
      <AdminConfig.Drawer name="my-drawer" element={<MyDrawer />} />
    </AdminConfig>
  );
};

export default MyDrawerConfig;

Open a Named Drawer

import { useOpenDrawer } from "webiny/admin";

const MyButton = () => {
  const { openDrawer } = useOpenDrawer();

  return <button onClick={() => openDrawer("my-drawer", { itemId: "abc" })}>Open Drawer</button>;
};

Stacking Drawers

const { openDrawer } = useOpenDrawer();

// Open first drawer
openDrawer("settings-drawer", { tab: "general" });

// Open second drawer on top
openDrawer("detail-drawer", { id: "123" });

Closing Drawers

import { useDrawers } from "webiny/admin";

const { closeNamedDrawer } = useDrawers();

closeNamedDrawer(); // close the topmost drawer
closeNamedDrawer("my-drawer"); // close a specific drawer by name

Typed Params with Zod

import { z } from "zod";
import { useDrawer } from "webiny/admin";

const paramsSchema = z.object({ itemId: z.string() });

const MyDrawer = () => {
  const { params, closeDrawer } = useDrawer(paramsSchema);
  // params.itemId is typed as string
};

Drawer API Reference

Hook / Component Import Purpose
AdminConfig.Drawer "webiny/admin/configs" Register a named drawer
useDrawer(schema?) "webiny/admin" Read params inside a named drawer
useOpenDrawer(schema?) "webiny/admin" Open a named drawer with params
useDrawers() "webiny/admin" Low-level access to openNamedDrawer / closeNamedDrawer

Key differences from dialogs:

  • Multiple drawers can be open at the same time (they stack)
  • Opening the same drawer name again while open is a no-op (prevents duplicates)
  • closeNamedDrawer() without args closes the topmost; with a name closes that specific drawer
  • Drawer sizes: "sm" (384px), "md" (520px), "lg" (640px), "xl" (1024px)

Lexical Editor Plugins

Admin extensions can also add custom plugins to the Lexical rich text editor used in both the Headless CMS and the Website Builder. These are registered as <Admin.Extension> and use imports from "webiny/admin/lexical", "webiny/admin/cms/lexical", and "webiny/admin/website-builder/lexical".

Quick Reference

White-label import:  import { AdminConfig } from "webiny/admin/configs";
Data list import:    import { ContentEntryListConfig } from "webiny/admin/cms/entry/list";
Form imports:        import { Bind, useForm, validation } from "webiny/admin/form";
UI imports:          import { Grid, Input, Select } from "webiny/admin/ui";
Register:            <Admin.Extension src={"/extensions/MyAdminExtension.tsx"} />
Develop:             yarn webiny watch admin
Deploy:              yarn webiny deploy admin

Related Skills

  • webiny-project-structure -- How to register Admin extensions
  • webiny-full-stack-architect -- Full-stack extension skeleton and registration
  • webiny-admin-architect -- Admin-side architecture patterns (headless + presentation features)
信息
Category 编程开发
Name webiny-admin-ui-extensions
版本 v20260825
大小 13.8KB
更新时间 2026-09-06
语言