Skip to content

DataViews & DataForm iteration for WordPress 6.9 #70936

Closed

Description

@oandregal

Next iteration: WordPress 7.0.

The work completed for WordPress 6.9 has been summarized below as comments: Field API, DataViewsPicker, DataForm, DataViews.

This is an iteration issue that tracks improvements to the DataViews and DataForm components planned for WordPress 6.9. See related overview issues for DataViews #55083 and DataForm #59745 and the tracking issues linked below.

DataViews

Layouts:

Filters:

Type New operators
array isAny, isNone, isAll, isNotAll
boolean is, isNot
date on, notOn, before, after, beforeInc, afterInc, inThePast, over, between
datetime on, notOn, before, after, beforeInc, afterInc, inThePast, over
email is, isNot, contains, notContains, startsWith, isAny, isNone, isAll, isNotAll
integer is, isNot, lessThan, greaterThan, lessThanOrEqual, greaterThanOrEqual, between
text is, isNot, contains, notContains, startsWith, isAny, isNone, isAll, isNotAll

Misc:

Field API

Field types:

Validation:

  • isValid.required and isValid.custom support in all controls. See status at #71500
  • isValid.elements #71194
  • Support async validation. #71161 and #71412

Misc:

DataForm

Layouts:

Controls:

DataPicker

Activity

  1. added
    [Feature] DataViewsWork surrounding upgrading and evolving views in the site editor and beyond
    [Type] IterationScoped iteration of an effort from a tracking issue or overview issue ideally for a major release.
    and removed on Jul 28, 2025
  2. moved this from 📥 Todo to 📋 Iteration/Tracking Issues in WordPress 6.9 Editor Taskson Aug 27, 2025
  3. oandregal commented on Oct 23, 2025

    @oandregal
    MemberAuthor

    Field API

    What follows are the major changes to the Field API that have been developed during the WordPress 6.9 cycle.

    For more information, consult the two reference documents:

    • the developer docs for API description
    • the storybook where users can interact with each field type in its different forms

    type

    Developer docs

    The field type has been expanded to describe the default field's behaviors for sorting, validation, filtering, editing, and rendering. The field author can still override those defaults, but they don't need to, they can have a working field by just providing the type.

    {
      id: 'fieldId',
      type: 'number'
    }

    We've also expanded the initial 3 types into 13:

    Field Type 6.8 6.9
    datetime ✓ ✓
    integer ✓ ✓
    text ✓ ✓
    array ✓
    boolean ✓
    color ✓
    date ✓
    email ✓
    media ✓
    number ✓
    password ✓
    telephone ✓
    url ✓

    Legend:

    • ✓ = Fully implemented with field type definition
    • Empty = Not available

    isValid

    Developer docs

    Validation has been completely overhauled to leverage a rule-base system. All field types support the required and elements rule, as well as defining custom validation via the custom function — that supports both sync and async modes. We plan to expand the available rules in future versions, follow-up #71500 for updates.

    // 6.8
    {
      isValid: ( item: Item, context?: ValidationContext ) => boolean;
    }
    
    // 6.9
    {
      isValid: {
        required: boolean,
        element: boolean,
        custom:
    		| ( ( item: Item, field: NormalizedField< Item > ) => null | string ) )
    		| ( (
    			item: Item,
    			field: NormalizedField< Item >
    		  ) => Promise< null | string > );
      }
    }

    setValue

    Developer docs

    The introduction of setValue, alongside the existing getValue, has added a few capabilities to the framework. See the related story: live, code.

    Working with nested data

    By using the dot notation in the field.id (e.g., user.profile.name), the getValue and setValue methods will automatically read and write the data from the corresponding object property — though field authors can still override getValue and setValue methods.

      const data = {
        user: {
          profile: {
            name: 'John Doe',
            email: 'john@example.com'
          }
        }
      };
    
      const fields = [
        {
          id: 'user.profile.name',  // Dot notation for nested access
          label: 'User Name',
          type: 'text',
        },
        {
          id: 'user.profile.email',
          label: 'Email',
          type: 'email',
        }
      ];

    Derived data

    setValue can be leveraged to decide which parts of the data should be updated, including modifying multiples parts of it.

      const data = {
        product: {
          price: 10,
          quantity: 3,
          total: 30  // Derived from price × quantity
        }
      };
    
      const fields = [
        {
          id: 'product.price',
          label: 'Price',
          type: 'integer',
          // Updates total when price changes
          setValue: ( { item, value } ) => ({
            product: {
              price: value,
              total: value * item.product.quantity
            }
          })
        },
        {
          id: 'product.quantity',
          label: 'Quantity',
          type: 'integer',
          // Updates total when quantity changes
          setValue: ( { item, value } ) => ({
            product: {
              quantity: value,
              total: item.product.price * value
            }
          })
        },
        {
          id: 'product.total',
          label: 'Total',
          type: 'integer',
          readOnly: true  // Calculated field, not editable
        }
      ];

    Bridge data and UI.

    In certain situations, the data and the UI control representing the data need to be adapted. Field authors can use getValue and setValue for this.

      const data = {
        user: {
          preferences: {
            notifications: true  // Boolean in data
          }
        }
      };
    
      const fields = [
        {
          id: 'notifications',
          label: 'Notifications',
          type: 'boolean',
          Edit: 'radio',
          elements: [
            { label: 'Enabled', value: 'enabled' },
            { label: 'Disabled', value: 'disabled' }
          ],
          // Transform boolean to string for UI
          getValue: ( { item } ) =>
            item.user.preferences.notifications === true
              ? 'enabled'
              : 'disabled',
          // Transform string back to boolean for data
          setValue: ( { value } ) => ({
            user: {
              preferences: {
                notifications: value === 'enabled'
              }
            }
          })
        }
      ];

    Edit

    Developer docs

    Field authors can define how a field behaves when it's edited. They can provide a custom edit control, but also select one of bundled ones — which have been expanded from 5 to 16:

    Control Description 6.8 6.9
    datetime Date time picker ✓ ✓
    integer Number input ✓ ✓
    radio Radio buttons ✓ ✓
    select Dropdown select ✓ ✓
    text Text input ✓ ✓
    array Multi-select ✓
    checkbox Checkbox control ✓
    color Color picker ✓
    date Date picker ✓
    email Email input ✓
    password Password input ✓
    telephone Phone input ✓
    textarea Multi-line text ✓
    toggle Toggle switch ✓
    toggleGroup Button group ✓
    url URL input ✓

    Additionally, some bundled edit controls now support a configuration option to control its behavior or look-and-feel.

     // Control the rows in a textarea
     {
        type: 'text',
        Edit: {
          control: 'textarea',
          rows: 5
        }
    }
      
    // Add prefixes/suffixes to control
    {
      type: 'text',
      Edit: {
        control: 'text',
        prefix: DollarPrefix, // React component
        suffix: USDSuffix, // React component
      }
    }
    Image

    filterBy

    Developer docs

    Filters have gained more operators (from 6 to 22) and they are now bound to the field type.

    The initial 6 operators available (is, isNot, isAny, isNone, isAll, isNotAll) have been expanded into 22 total:

    • for integer and number: lessThan, greaterThan, lessThanOrEqual, greaterThanOrEqual, between
    • for date and datetime: before, after, beforeInc, afterInc, on, notOn, inThePast, over, between
    • for text: contains, notContains, startsWith

    The addition of new field types and edit controls has also expanded the filters available, that are now dependant on the field type, and they include support for user input as well:

    User input Date filter
    ImageImage Image Image

    getElements

    Developer docs

    {
        getElements: () => Promise<Option[]>
    }

    We've introduced a getElements async function that fetches elements on demand, only when they are needed. This is a new API that will continue to evolve, follow-up changes at #70834

    This API can be seen in use in the Pages screen of the Site Editor. The author filter and edit control won't fetch the authors until the user interacts with those controls.

    readOnly

    We've added a readOnly property (boolean) to mark fields that cannot be edited. In edit contexts, they'll be displayed using the render property. For example, this can be useful when displaying non-editable metadata (file size, etc.).

  4. andrewserong commented on Oct 23, 2025

    @andrewserong
    Contributor

    @oandregal I see you're doing updates for 6.9. Here's a quick draft of things we might include for DataViewsPicker, if it helps? FYI: @talldan and @tellthemachines — feel free to edit if I got any of this wrong!


    DataViewsPicker Overview

    DataViewsPicker is a selection-focused variant of DataViews designed for building item picker interfaces. It extends the familiar DataViews API with selection management and action buttons, making it ideal for workflows where users need to browse and pick items from a dataset. While this component isn't used directly in Gutenberg as yet, possible use cases could include a media picker for selecting images (#71944), or a generalised post picker for selecting posts or page parents (#71128). It currently only supports a grid layout view.

    Key Features

    • Selection Management: Track which items users have selected via selection state and onChangeSelection callbacks
    • Action Buttons: Include action buttons (like "Confirm" or "Cancel") that operate on selected items via the actions prop
    • Familiar DataViews API: Same field definitions, filtering, pagination, and view configuration as DataViews
    • Customizable Layouts: Supports DataViews picker-specific layouts (to begin with, there is just a grid picker view available)
    • Default UI: Like DataViews, uses a pre-built interface including search, filters, layout switcher, and action toolbar—or use custom children to build your own

    Basic Usage

    <DataViewsPicker
      data={items}
      fields={fields}
      selection={selectedIds}
      onChangeSelection={setSelectedIds}
      view={view}
      onChangeView={setView}
      paginationInfo={{ totalItems: 100, totalPages: 10 }}
      actions={[
        { id: 'cancel', label: 'Cancel', callback: () => {...} },
        { id: 'confirm', label: 'Confirm', isPrimary: true, callback: () => {...} },
      ]}
      defaultLayouts={{ [LAYOUT_PICKER_GRID]: {} }}
    />

    Sub-Components

    For custom UI layouts, DataViewsPicker exposes sub-components like Search, Filters, Layout, Pagination, and BulkActionToolbar that can be composed together.

  5. oandregal commented on Oct 24, 2025

    @oandregal
    MemberAuthor

    DataViews

    What follows are the major changes to the DataViews component that have been developed during the WordPress 6.9 cycle. See related Field API changes.

    For more information, consult the two reference documents:

    view

    Developer docs

    Group by

    All the bundled layouts –table, grid, and list– support now grouping the items by a given field by declaring a groupByField in the view. Follow issue for updates, and take a look at the storybook.

    {
     groupByField: 'fieldId'
    }
    grid list table
    Image Image Image

    Locked filters

    Locked filters are filters that cannot be edited or removed by the user. They appear in the filters UI but are marked as read-only, ensuring certain filtering conditions are always applied.

    {
      filters: [ {
        field: 'status',
    	operator: OPERATOR_IS_ANY,
    	value: 'publish',
    	isLocked: true,
      } ]
    }
    Image

    Table layout: align and lock movers

    There are two new options that apply to the table layout:

    {
      type: 'table',
      layout: {
        styles: {
          fieldId: {
            align: 'start' | 'center' | 'end'
          }
        },
        enableMoving: false
      }
    }
    • align is a new property to control the horizontal alignment of content within the cells. It's specificied per field in the layout.
    • enableMoving: boolean to control whether the fields can be moved around. True by default. If false, it removes the moving controls in the table header and disables moving the fields via the view config as well.
    Table View config
    Image Image

    actions

    Developer docs

    The modal actions have now more options to control its behavior:

    • modalHeader: string | ( ( items: Item[] ) => string );. Can now take a function to dynamically set the header based on the selected items.
    • modalSize: small | medium | large | fill. Controls the size of the modal.
    • modalFocusOnMount: true | false | firstElement | firstContentElement. Controls the focus behavior when the modal opens.

    paginationInfo (infinite scroll)

    Developer docs

    All bundled layouts -table, list, and grid- now support infinite scroll. It's enabled per view type, and DataViews takes a infiniteScrollHandler function that listens to scroll events and handles the data retrieval. See storybook live and implementation for details.

    const view = {
      layout: 'grid',
      infiniteScrollEnabled: boolean
    }
    
    const paginationInfo = {
      totalItems: totalItems,
      totalPages: totalPages,
      infiniteScrollHandler: () => { /* Fetch data, update view, etc. */ }
    };
    
    return (
      <DataViews
        view={view}
        paginationInfo={paginationInfo}
    );
    2510-dataviews-wp-6.8-vs-6.9-dataviews-infinite-scroll.mov

    renderItemLink

    Developer docs

    The renderItemLink prop lets you provide a custom component that wraps clickable fields. It can render regular links, but also allows integration with routing libraries (like TanStack Router or React Router).

    The component receives the following props:

    • item: The data item that was clicked
    • Additional standard HTML anchor props (className, style, etc.)
    <DataViews
        renderItemLink={ ( { item, ...props } ) => (
            <Link to={ `/sites/${ item.slug }` } preload="intent" { ...props } />
        ) }
    />

    config

    Developer docs

    Prop to configure the list of perPageSizes available in the view config. Defaults to [10, 20, 50, 100]).

    Image

    empty

    Developer docs

    An element to display when the data prop is empty. Defaults to <p>No results</p>.

    children

    Developer docs, Storybook

    We've exported many of the subcomponents DataViews is made up of, for them to be reused to build UIs.

    When DataViews receives no children, it renders its complete built-in UI (search bar, filter controls, view config, layout, etc.):
      

    <DataViews
      data={ posts }
      fields={ fields }
      view={ view }
      onChangeView={ setView }
      actions={ actions }
      paginationInfo={ paginationInfo }
    />

    When children are provided, you can build custom layouts while leveraging DataViews' internal state management and data handling logic:

      <DataViews
        data={ posts }
        fields={ fields }
        view={ view }
        onChangeView={ setView }
        paginationInfo={ paginationInfo }
      >
        <DataViews.Search />
        <DataViews.Layout />
        <DataViews.Pagination />
      </DataViews>

    DataViews exposes 9 subcomponents for free composition:

    Component Purpose
    DataViews.BulkActionToolbar Toolbar for bulk actions
    DataViews.Filters Filter controls panel
    DataViews.FiltersToggle Button to show/hide filters panel
    DataViews.FiltersToggled Badge showing active filters count
    DataViews.Footer Component that renders pagination and bulk actions
    DataViews.Layout Renders the actual data (grid/table/list)
    DataViews.LayoutSwitcher Buttons to switch between layouts
    DataViews.Pagination Page navigation controls
    DataViews.Search Search input field
    DataViews.ViewConfig Button to open view configuration
  6. oandregal commented on Oct 24, 2025

    @oandregal
    MemberAuthor

    DataForm

    What follows are the major changes to the DataForm component that have been developed during the WordPress 6.9 cycle. See related Field API changes.

    For more information, consult the two reference documents:

    form

    Developer docs

    The form API has been updated to accommodate the changes to existing layouts and the introduction of two new ones: card and row.

    panel

    Developer docs, Storybook

    There are two new properties, openAs and summary, to configure the behavior of the layout.

    {
      layout: {
        type: 'panel',
        labelPosition: 'top' | 'side' | 'none',
        openAs: 'dropdown' | 'modal',
        summary: 'summaryField' | [ 'summaryFieldOne', 'summaryFieldTwo' ]
      }
    }
    • openAs: determines how the panel opens, either as a dropdown (default) or a modal.
    • summary: controls what fields summarize the panel, while before the first children was used.
    Image

    card

    Developer docs, Storybook

    The new card layout is a container layout: it wraps other layouts (regular, panel, etc.) within a card component.

    • withHeader: controls whether the card has a header
    • isOpened: controls how the card loads (opened or closed)
    • summary: a mechanism to render fields in the header, and control when they are displayed, either when the card is collapsed (the default) or always
    // Card open and summary displayed only when collapsed
    {
      layout: {
        type: 'card',
        summary: 'plan-summary'
      },
    }
    
    // Card with no header
    {
      layout: {
    	type: 'card',
    	withHeader: false,
      },
    } 
    
    // Card collapsed with summary
    {
      layout: {
    	type: 'card',
    	isOpened: false,
    	summary: [ { id: 'dueDate', visibility: 'always' } ],
      },
    }
    Image

    row

    Developer docs, Storybook.

    The new row layout is a container layout: it enables positioning other layouts horizontally.

    • alignment: control vertical alignment of fields within the row
    • styles: provides flex styles, for the width to be adjusted per field
    {
      layout: {
        type: 'row',
        alignment: 'start' | 'center' | 'end',
        styles: {
          "fieldId": {
            flex: CSSFlexProperties
          }
        }
      }
    }
    Image

    validity

    Validation has been revamped in 6.9: it's now controlled (DataForm has a validity prop), and supports asynchronous validation. All the DataForm controls gained support for all validation rules (required, elements, and custom). See related Field API updates and the Field API validation issue.

    There's a new useFormValidity hook to compute the validity prop, and the isItemValid utility has been removed.

    const fields = [
      {
        id: 'title',
        type: 'text',
        label: 'Text',
        isValid: {
          required: true,
          custom: ( item: Item, field: NormalizedField< Item > ) => {
            // returns a validation message if field is invalid
          }
        }
      },
      // other fields
    ];
    
    /*
     * validity: object, represents the validation state of the form
     * isValid: boolean, whether the form is valid or not
     */
    const { validity, isValid } = useFormValidity( data, fields, form );
    
    return (
    	<form>
    		<VStack alignment="left">
    			<DataForm< Item >
    				data={ data }
    				fields={ fields }
    				form={ form }
    				validity={ validity }
    				onChange={ onChange }
    			/>
    			<Button disabled={ ! isValid } >
    				Submit
    			</Button>
    		</VStack>
    	</form>
    );
    2510-dataviews-wp-6.8-vs-6.9-dataform-validation.mov
  7. oandregal commented on Oct 24, 2025

    @oandregal
    MemberAuthor

    This issue is now complete, so I'm closing it. We may continue to share updates for WordPress 6.9 Field Guide (added them to issue description). I'll share here the follow-up iteration for 7.0 when it's ready.

  8. moved this from 📋 Iteration/Tracking Issues to ✅ Done in WordPress 6.9 Editor Taskson Oct 24, 2025
  9. oandregal commented on Oct 24, 2025

    @oandregal
    MemberAuthor

    FWIW, in the WordPress 6.9 cycle, there's been 154 commits done by ~37 different authors to the @wordpress/dataviews package. 🥳

    Commit count:

    git log --oneline wp/6.8..wp/6.9 -- packages/dataviews |wc -l

    Unique authors count. It counts unique emails, there may be duplicates if some person used different emails:

    git log --format="%ae" wp/6.8..wp/6.9 -- packages/dataviews | sort -u | wc -l
  10. oandregal commented on Nov 7, 2025

    @oandregal
    MemberAuthor

    I've bootstrapped the next iteration issue for WordPress 7.0 at #73076 It's a live issue: plans and ongoing work will be consolidated as we progress. Subscribe there for updates. Added previous/next links in the issue description, for discoverability.

  11. added
    Needs Dev NoteRequires a developer note for a major WordPress release cycle
    on Nov 11, 2025
  12. oandregal commented on Nov 11, 2025

    @oandregal
    MemberAuthor
  13. added
    has dev notewhen dev note is done (for upcoming WordPress release)
    and removed
    Needs Dev NoteRequires a developer note for a major WordPress release cycle
    on Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    [Feature] DataViewsWork surrounding upgrading and evolving views in the site editor and beyond[Type] IterationScoped iteration of an effort from a tracking issue or overview issue ideally for a major release.has dev notewhen dev note is done (for upcoming WordPress release)

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions