Scoped, themeable React component library for WordPress plugins. Built on ShadCN patterns, Tailwind CSS v4, and Base-UI primitives.
src/
├── components/
│ ├── ui/ # Core ShadCN-style components (150+ exports)
│ ├── settings/ # Schema-driven settings system
│ └── wordpress/ # WordPress integration (Layout, DataViews)
├── providers/ # ThemeProvider (CSS variable injection)
├── themes/ # Built-in theme presets
├── hooks/ # useMobile, useWindowDimensions
└── lib/ # Utilities (cn, renderIcon, WpMedia, wordpress-date)
// Main entry (includes styles)
import { Settings, Button, ThemeProvider } from '@wedevs/plugin-ui';
// Sub-path exports
import { Settings } from '@wedevs/plugin-ui/settings';
import { Button, Input } from '@wedevs/plugin-ui/components/ui';
import { ThemeProvider } from '@wedevs/plugin-ui/providers';
import { defaultTheme, createTheme } from '@wedevs/plugin-ui/themes';
import { cn } from '@wedevs/plugin-ui/utils';
// Styles (import in your entry point)
import '@wedevs/plugin-ui/styles.css';@import "tailwindcss";
@import "@wedevs/plugin-ui/styles.css" layer(plugin-ui);Schema-driven settings page with hierarchical navigation, dependency evaluation, validation, and WordPress hook extensibility.
page → subpage → tab → section → subsection → field / fieldgroup
import { Settings } from '@wedevs/plugin-ui';
<Settings
schema={settingsSchema} // SettingsElement[] (flat or hierarchical)
values={values} // Record<string, any> keyed by dependency_key
onChange={(scopeId, key, value) => {
setValues(prev => ({ ...prev, [key]: value }));
}}
onSave={async (scopeId, treeValues, flatValues) => {
// treeValues: nested object built from dot-separated keys
// e.g. { dokan: { general: { store_name: "..." } } }
// flatValues: original flat dot-keyed values
// e.g. { "dokan.general.store_name": "..." }
await api.post(`/settings/${scopeId}`, treeValues);
}}
renderSaveButton={({ dirty, hasErrors, onSave }) => (
<Button onClick={onSave} disabled={!dirty || hasErrors}>Save</Button>
)}
hookPrefix="my_plugin" // WordPress filter hook prefix
applyFilters={applyFilters} // @wordpress/hooks applyFilters for field extensibility
/>dependency_key: Unique key on each field element, used as the key invaluesandflatValues- Dependencies: Elements can conditionally show/hide based on other field values via
dependenciesarray - Validation: Per-field
validationsarray with rules and error messages - Dirty tracking: Per-scope (subpage/page) dirty state; resets only on successful save
- Error handling: If
onSavethrows{ errors: { fieldKey: "message" } }, errors display on the relevant fields - Extensibility:
applyFiltersenables WordPress hooks like{hookPrefix}_settings_{variant}_field
import { useSettings } from '@wedevs/plugin-ui';
const {
values, // All current field values
activePage, // Current page element
activeSubpage, // Current subpage element (if any)
activeTab, // Current tab element (if any)
isPageDirty, // (pageId) => boolean
getPageValues, // (pageId) => Record<string, any>
errors, // Record<string, string> validation errors
} = useSettings();import { ThemeProvider, createTheme } from '@wedevs/plugin-ui';
// Use a built-in preset
<ThemeProvider theme={defaultTheme}>
<App />
</ThemeProvider>
// Create a custom theme
const myTheme = createTheme({
primary: '220 90% 56%', // HSL values (without hsl() wrapper)
background: '0 0% 100%',
foreground: '0 0% 3.9%',
// ... see ThemeTokens type for all available tokens
});Built-in presets: defaultTheme, slateTheme, amberMinimalTheme, t3ChatTheme, midnightBloomTheme, bubblegumTheme, cyberpunkTheme, twitterTheme (each with a dark variant).
Input, Textarea, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, DatePicker, DateRangePicker, Calendar, CurrencyInput, InputOTP, RichTextEditor, FileUpload
Card variants: CheckboxCard, RadioCard, SwitchCard
Labeled variants: LabeledCheckbox, LabeledRadio, LabeledSwitch
Card, Tabs, Separator, ScrollArea, Layout (WordPress sidebar+content), Sidebar (shadcn primitives), Field (form control wrapper with label, description, error)
Badge, Avatar, AvatarGroup, Progress, CircularProgress, Skeleton, Spinner, Thumbnail, DataViews (WordPress DataViews wrapper)
Modal, Sheet, Popover, Tooltip, DropdownMenu, AlertDialog, Toaster (sonner toast)
Alert, Notice, toast() (from sonner)
Layout (sidebar layout with WordPress hook integration), DataViews (wraps @wordpress/dataviews with filter hooks), LayoutMenu (navigation menu)
import { Layout, LayoutHeader, LayoutBody, LayoutSidebar, LayoutMain } from '@wedevs/plugin-ui';
<Layout namespace="my-plugin">
<LayoutHeader>Header</LayoutHeader>
<LayoutBody>
<LayoutSidebar menuItems={menuItems} />
<LayoutMain>Content</LayoutMain>
</LayoutBody>
</Layout>import { DataViews } from '@wedevs/plugin-ui';
<DataViews
namespace="my-plugin"
data={items}
fields={fields}
actions={actions}
paginationInfo={paginationInfo}
/>Supports WordPress filter hooks: {snakeNamespace}_dataviews_{elementName}
- Composition pattern: All components use compound component pattern (e.g.,
Card+CardHeader+CardContent) cn()utility: Use for merging Tailwind classes — combinesclsx+tailwind-mergeFieldwrapper: Use to wrap form controls with consistent label, description, and error display- No WordPress dependency in UI components: Only
Layout,DataViews, andSettings(viaapplyFilters) touch WordPress APIs - Externals: React, ReactDOM, and WordPress packages (
@wordpress/components,@wordpress/dataviews,@wordpress/hooks,@wordpress/i18n,@wordpress/date) are externalized — consumers must provide them
GitHub CI runs these checks on PRs to main. Run them locally before pushing to avoid failures:
npm run lint # ESLint (src/**/*.{ts,tsx})
npm run typecheck # tsc --noEmitBoth must pass. The CI pipeline (.github/workflows/ci.yml) runs these on ubuntu-latest with Node 24.
src/components/settings/Settings.mdx— Full settings API referenceDEVELOPER_GUIDE.md— WordPress integration guidesrc/DeveloperGuide.mdx— Storybook developer guide