Refactoring plugin settings with the WordPress DataForm API

WordPress 6.9 and the Site Editor work are not only a theme developer’s concern. The admin interface itself is being restructured. If you have built settings pages the old way, stacking PanelBody inside PanelBody with a PanelRow for every control, you know how that ages. Adding one field means going back into a pile of imperative JSX.

Core now offers a declarative option. Here is how to use the DataForm component for DataForm plugin settings pages. It is the same tech behind the new Site Editor experience, and after a configuration-driven UI, hand-wrapping TextareaControl tags loses its appeal fast.

Why DataForm matters for admin UI

Traditional WordPress React development asks for a lot of boilerplate before a simple form renders. Import every control, track each one’s state, lay it all out by hand. It breaks easily. For more on what else changed, I wrote about why WordPress 6.9 is a major win for developers.

With DataForm you define your fields as a JSON-like configuration object. The component renders them, mapping each one to the right UI control based on its data type. It also handles layout types such as cards and panels without extra work on your side.

Step 1: installing dependencies

You need the @wordpress/dataviews package. If you already build with wp-scripts, it drops straight in. Run this in your plugin directory:

npm install @wordpress/dataviews --save

One gotcha on the import. Import from @wordpress/dataviews/wp so that WordPress resolves the dependencies correctly, as the official package documentation notes.

import { DataForm } from '@wordpress/dataviews/wp';

Step 2: defining your fields configuration

Rather than rendering components, you define a fields array where each object is one setting. For DataForm plugin settings pages you set the id, label and type. Want a particular UI control, like a toggle or a textarea? Set the Edit property.

const fields = [
    {
        id: 'message',
        label: __( 'Announcement Message', 'my-plugin' ),
        type: 'text',
        Edit: 'textarea',
    },
    {
        id: 'display',
        label: __( 'Display Bar', 'my-plugin' ),
        type: 'boolean',
        Edit: 'toggle',
    },
    {
        id: 'size',
        label: __( 'Font Size', 'my-plugin' ),
        type: 'text',
        Edit: 'toggleGroup',
        elements: [
            { value: 'small', label: __( 'Small', 'my-plugin' ) },
            { value: 'medium', label: __( 'Medium', 'my-plugin' ) },
            { value: 'large', label: __( 'Large', 'my-plugin' ) },
        ],
    },
];

Step 3: layout and grouping

Grouping is where DataForm earns its keep. Wrap fields into sections and pick a regular, panel, card or row layout. The card layout is the one that gets closest to the modern Gutenberg sidebar look.

const form = {
    fields: [
        {
            id: 'general-settings',
            label: __( 'General', 'my-plugin' ),
            children: [ 'message', 'display' ],
            layout: { type: 'card', withHeader: false },
        },
        {
            id: 'appearance-settings',
            label: __( 'Appearance', 'my-plugin' ),
            children: [ 'size' ],
            layout: { type: 'card', isOpened: false },
        },
    ],
};

Step 4: connecting to state

Three settings used to mean three useState hooks. With DataForm everything lives in one object, which matches how WordPress stores settings in the REST API when you register the setting with the object type.

const SettingsPage = () => {
    const { settings, setSettings, saveSettings } = bbioon_useSettings();

    return (
        <DataForm
            data={ settings }
            fields={ fields }
            form={ form }
            onChange={ ( edits ) =>
                setSettings( ( current ) => ( {
                    ...current,
                    ...edits,
                } ) )
            }
        />
    );
};

The onChange callback hands you only the edits, meaning the fields that changed. That is why the spread operator merges the updates into current state instead of replacing it, and why an update does not wipe your other settings.

The takeaway

Moving to DataForm plugin settings pages buys you more than a modern look. You get an architecture that follows where WordPress Core is going, and you spend your time on the data logic of your plugin instead of fighting CSS.

If DataForm is eating your dev hours, I can take that off your plate. I have been wrestling with WordPress since the 4.x days.

For deeper documentation on validation or asynchronous elements, read the WordPress 6.9 DataViews announcement. Then go ship something clean.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.