WordPress.org

WordPress Developer Blog

Using Data Views to display and interact with data in plugins

Using Data Views to display and interact with data in plugins

Have you noticed the new User Interface (UI) in the Site Editor for entities such as Pages, Templates, or Patterns? This new UI provides a more unified experience for managing and navigating data. With this new interface, users can filter information, customize views, and select specific fields—all in one, consistent environment.

  • Snapshot of "Site Editor > Templates" section using DataViews component
  • Snapshot of "Site Editor > Pages" section using DataViews component
  • Snapshot of "Site Editor > All Patterns" section using DataViews component

As WordPress transitions into Phase 3: Collaboration, an ongoing effort is underway to enhance the admin experience and incorporate a new visual language across the platform.

But what’s the magic behind this new interface? It’s no magic at all—just the powerful and versatile DataViews component.

The DataViews component (check the original proposal) provides a powerful API to render datasets using different layouts such as tables, grids, or lists. Additionally, users can customize the data in many ways: filtering, searching, pagination, grouping, sorting, field management, performing actions, and more.

Pretty impressive for just one component, right?

Now that you know WordPress Core is using Data Views to improve and consolidate the admin UI, you might be wondering: Could I also use Data Views in my projects? How about in my plugins? Yes, you can! Even though the component is still being refined, you can start using it in your own plugins.

In this article, you’ll learn how to do just that.

DataViews is still considered an experimental component, and breaking changes are expected at its current stage. Any breaking changes to this component will be communicated through its Changelog.

Let’s build a plugin!

Since the DataViews component is all about managing datasets, let’s use it in a plugin to display some data.

First, let’s take a list of images from a JSON file and display them on several layouts. Next, using only the component’s built-in features, let’s provide the tools so users can decide how to display the list of images and perform actions on them.

But the project won’t stop there. In an article to come, I’ll guide you through adding functionality to let users add these images to the Media Library. 

Throughout this whole process, you’ll learn how to:

  • Add a custom React app to the admin screens.
  • Leverage the DataViews component to display datasets. 
  • Add actions that enable users to upload selected images to the Media Library.
  • Build a user-friendly UI that shows your users what they’re doing, as they do it.

This article will show you how to use DataViews component from a plugin. But the project started in this article will be expanded in a second article that will show you how to integrate the images displayed in the Media Library. 

Here’s a video showing what the app you’ll create in this article will look like:

Before you start

To build this plugin, you’ll need a proper Block Development Environment on your machine, including the installation of Node.js and a Local WordPress environment.

You should also have a good grasp of JavaScript and React and be familiar with the @wordpress/scripts package, especially how to run the start and build commands to process JavaScript and create bundled assets.

The final code of the project explained in this article is available at https://github.com/wptrainingteam/devblog-dataviews-plugin
Throughout the article, you’ll find links to specific commits corresponding to the changes being explained, to help you track the project’s progress.

Loading a React app in an admin screen

Let’s start by building a plugin that loads a minimal React app on a page in the WordPress admin. 

The How to use WordPress React components for plugin pages blog post covers how to create a plugin that loads a React app on an admin page, so please refer to that post for additional explanations. The Create your First App with Gutenberg Data guide in the Block Editor Handbook is another great example of a React app in the admin.

To create a basic plugin:

  1. Go to the plugins directory.
  2. Create a new folder named devblog-dataviews-plugin.
  3. Inside this folder, create a file named plugin.php.
  4. Add the plugin header to the plugin.php file.
<?php

/**
 * Plugin Name: Dataviews Images Media
 * Description: Displays a dataset of images for upload to the Media Library.
 * Version: 0.1.0
 * Requires at least: 6.6
 * Text Domain: dataviews-images-media
 */

defined( 'ABSPATH' ) || exit;

Next, activate the plugin DevBlog Data Views Plugin from the WordPress admin dashboard.

After activation:

  1. Inside the main plugin folder, create a src directory.
  2. Within the src folder, create an empty index.js file.
  3. In the main plugin folder, create a package.json file with the following content.
{
  "name": "devblog-dataviews-plugin",
  "version": "0.1.0",
  "author": "The WordPress Contributors",
  "description": "Plugin with Admin menu option and dataviews",
  "license": "GPL-2.0-or-later",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "devDependencies": {
    "@wordpress/scripts": "^28.3.0"
  }
}

Open your terminal and run npm install to install the project’s dependencies. 

With the plugin’s groundwork laid out, now add the necessary code to plugin.php file to create a new Media subpage called Add Media from third-party service

add_action( 'admin_menu', 'devblog_dataviews_admin_menu' );


/**
 * Creates a new Media subpage and set the HTML for it.
 */
function devblog_dataviews_admin_menu() {
	add_media_page(
		__( 'Add Media from third party service', 'devblog-dataviews-plugin' ),
		__( 'Add Media from third party service', 'devblog-dataviews-plugin' ),
		'manage_options',
		'add-media-from-third-party-service',
		function () {
			printf(
				'<h1>%s</h1><div id="add-media-from-third-party-service"></div>',
				esc_html__( 'Add Media from third party service', 'devblog-dataviews-plugin' )
			);
		}
	);
}

Next, add the necessary code to the plugin.php file to load the build/index.js on the Add Media from third-party service page (this will attach the React app to this page).

add_action( 'admin_enqueue_scripts', 'devblog_dataviews_admin_enqueue_assets' );

/**
 * Enqueues JS and CSS files for our custom Media subsection page.
 *
 * @param string $hook_suffix The current admin page.
 */
function devblog_dataviews_admin_enqueue_assets( $hook_suffix ) {
	// Load only on ?page=add-media-from-third-party-service.
	if ( 'media_page_add-media-from-third-party-service' !== $hook_suffix ) {
		return;
	}

	$dir = plugin_dir_path( __FILE__ );
	$url = plugin_dir_url( __FILE__ );

	$asset_file = $dir . 'build/index.asset.php';

	if ( ! file_exists( $asset_file ) ) {
		return;
	}

	$asset = include $asset_file;

        wp_enqueue_script(
		'devblog-dataviews-script',
		$url . 'build/index.js',
		$asset['dependencies'],
		$asset['version'],
		array(
			'in_footer' => true,
		)
	);
}

Last, create the Javascript code to attach a minimal React app to a specific ID on the HTML of the Add Media from third-party service page.

You can do that by adding the following code to src/index.js:

import domReady from '@wordpress/dom-ready';
import { createRoot } from '@wordpress/element';

const App = () => {
  return (
    <div>
      <p>Our React app</p>
    </div>
  );
};

domReady( () => {
	const root = createRoot(
		document.getElementById( 'add-media-from-third-party-service' )
	);
	root.render( <App /> );
} );

Run npm start to start the build process with the wp-scripts package and watch for changes in the code. You should see an index.js generated in the build/ folder.

At this point, you should have a boilerplate code in your project to display a React app in the WordPress admin and see a new page under Media called Add Media from third party service and the output of a minimal React app in that subsection.

The project is ready to use the DataViews component in the React app. 

Displaying a dataset with Data Views

To use the DataViews component in the React app, you must install the @wordpress/dataviews package and import it into your app.

You can install this package and add it as a dependency of the project with the following command from the root of the project:

npm i @wordpress/dataviews --save

Now create an App.js file under src folder with a new version of the main App component:

import { DataViews } from '@wordpress/dataviews' ;


// source "data" definition 
// "defaultLayouts" definition 
// "fields" definition 


const App = () => {
    // "view" and "setView" definition
    // "processedData" and "paginationInfo" definition
    // "actions" definition 


    return (
        <DataViews
            data={ processedData }
            fields={ fields }
            view={ view }
            onChangeView={ setView }
            defaultLayouts={ defaultLayouts }
            actions={ actions }
            paginationInfo={ paginationInfo }
        />
    );
};


export default App;

This new file defines an App component and exports it to be used elsewhere. 

For now, this App component just imports the DataViews component and uses it with some yet-to-be-defined values passed to its props:

  • data – A one-dimensional array of objects representing the dataset to work with. This data must be processed and updated to reflect the applied filtering, sorting, and pagination options.
  • fields – The display settings and capabilities for each record in the dataset.
  • view – The configuration of how the dataset is displayed to the user.
  • onChangeView – The callback that will be called every time the view config changes.
  • defaultLayouts – The view types that are available for the user. 
  • actions – Collection of operations that can be performed upon each record.
  • paginationInfo – Total number of items in the dataset and total number of pages.

Before you can see something meaningful on the screen, you need to define all these values required by the DataViews component. But before defining these values, there are a few things you must take care of.

First, import the new App component from this file instead of the temporary one you created earlier. Do that from src/index.js:

import domReady from '@wordpress/dom-ready';
import { createRoot } from '@wordpress/element';

import App from './App';

domReady( () => {
	const root = createRoot(
		document.getElementById( 'add-media-from-third-party-service' )
	);
	root.render( <App /> );
} );

Now, there’s an important thing that needs to be provided to the DataViews component: its styles.

The styles

The DataViews component uses several components of the @wordpress/componentspackage, so styles from both @wordpress/dataviews and @wordpress/components packages must be loaded wherever you use theDataViews component.

You can use the wp-components handle to load the styles for all the @wordpress/component package components. But for the @wordpress/dataviewsstyles, at this component’s stage, you must import them directly from the node_modules folder.

Create a src/style.scss file in your plugin and add the following code:

@import "@wordpress/dataviews/build-style/style.css";


#add-media-from-third-party-service {
  background: white;
  .topic_photos {
    display: flex;
    flex-wrap: wrap;
    span {
      background-color: rgb(166, 165, 165);
      color: white;
      padding: 2px 4px;
      margin: 2px;
    }
  }
}

This code imports the @wordpress/dataviews styles and adds some extra styles for your project.

You need to generate the final CSS file from this CSS file, which will be loaded on the same page the React app lives on and contain the styles of @wordpress/dataviews.

To generate the final CSS file, import src/style.scssfrom the src/index.js file

import "./style.scss";

With npm start running , you’ll see a new style-index.css  file generated under the build folder.

This generated file is the one you can enqueue in WordPress. Add the following code at the end of the devblog_dataviews_admin_enqueue_assets function in plugin.php:

wp_enqueue_style(
  'devblog-dataviews-styles',
  $url . 'build/style-index.css',
  array( 'wp-components' ),
  $asset['version'],
);

This code enqueues the generated build/style-index.css, which contains the DataViews component styles, in the page where the React app is located. Additionally, it loads the @wordpress/components styles for that page by adding wp-componentsas a dependency.

Now it’s time to define the values needed for the DataViews component’s props. Let’s start with the data prop! 

The DataViews component is a very powerful and versatile component that requires a lot of input data through its props to work properly. You won’t be able to see the Data Views in action until you define the values for its props in the following section. Have some patience until then. It’ll be worth it.

The DataViews props

The data

The most important piece that the DataViews component needs is data to display.

For this project, you’ll work with a local JSON file that contains a data list of photos. Each item from the list contains information about a photo, such as a description, an author, several URLs, and related topics.

For the sake of simplicity this project uses a local JSON file to get the dataset to work with, but a great use case for the DataViews component is connecting it to a third-party REST API, allowing for dynamic data integration within WordPress. 

This is an example of an item contained in the data list used for this project:

dataPhotos = [
{
   id: "nwPxPBWY5JI",
   slug: "a-blue-bird-sitting-on--nwPxPBWY5JI",
   width: 2818,
   color: "#c0c0d9",
   height: 4235,
   alt_description: "a blue bird sitting on top of a plant",
   urls: {
     raw: "https://images.unsplash.com/photo-raw-...",
     full: "https://images.unsplash.com/photo-full-...",
     regular: "https://images.unsplash.com/photo-regular-...",
     small: "https://images.unsplash.com/photo-small-...",
     thumb: "https://images.unsplash.com/photo-thumb-...",
     small_s3: "https://images.unsplash.com/photo-small_s3-...",
   },
   topics: ["health", "travel"],
   user: {
      first_name: "Clayton",
      last_name: "Chase",
      url: "https://chasemade.com",
    },
},
{...},
...
]

Copy and paste the content of the file located here into a src/data.js file and import the content of that file into a dataPhotos constant in src/App.js.

import { dataPhotos } from './data';

The data is ready for the project to be displayed with Data Views.

The fields

Next, you must define how the fields will be displayed in the DataViews component and the specific features you want to enable for each field.

To do that, the fields prop of the DataViews component expects an array of objects, where each object contains the configuration settings for each field. Some of the properties you can use to define a field’s configuration are:

  • id: identifier for the field.
  • label: the field’s name to be shown in the UI.
  • getValue: a function that returns the field’s value.
  • render: a custom React component to render the field. 
  • elements: the set of valid values to filter by this value. Setting this value enables the filters by this value.
  • type: the type of the field. 
  • enableSorting: whether the data can be sorted by the given field.
  • enableHiding: whether the field can be hidden.
  • enableGlobalSearch: whether the field is searchable.
  • filterBy: configuration for the filters.
    • operators: the list of operators supported by the field.
    • isPrimary: whether it is a primary filter. 

Check the official documentation on the fields prop of the DataViews component for full info on each one of these properties

You must first decide which capabilities to provide to the data inside the DataViews component. This means you need to decide which fields can be sorted, filtered, hidden, and searched.

  • Sorting: By default, all fields are sortable. To exclude specific fields from sorting, set enableSorting to false for those fields. This project enables sorting for the Image and Topics fields.
  • Searching: By default, no fields are searchable. To enable search functionality for specific fields, set enableGlobalSearch to true.  This project enables search on the ID, Author, and Description fields.
  • Hidable: By default, all fields are hideable. To exclude specific fields from being hidden, set enableHiding to false for those fields. This project disables hiding for the Image  field.
  • Filter by: To enable filtering by a field, you have to pass a value to the elementsproperty of the field. The expected value is an array of objects with properties label and value. With the filterBy property, you can fine-tune this filtering behavior by defining operators and isPrimary properties. This project enables filters by the Topics field.

To enable filters by the topics fields, we have a challenge to solve with the current project’s raw data. The topic values for each photo item come in the following format:

[
  { topics: ["nature", "water"] }, 
  { topics: ["nature", "mountain"] }
]

But the elements prop expects something like the following:

[
  { label: "Nature", value: "nature" }, 
  { label: "Water", value: "water" }, 
  { label: "Mountain", value: "mountain" }
]

The elements prop expects an array of unique topics, each described by a label and a value property.

The need to adapt your data when setting filters for the DataViews component may come up a lot. So, let’s write a Javascript function that does the following:

  • Gets a list of unique topics from a list of arrays with repeated topics.
  • Converts the topics to the right format expected by the elements property for the fields prop.

Create a src/utils.js file and add the following exportable getTopicsElementsFormat function.

/**
 * Retrieves the unique topics from an array of photos.
 */
export const getTopicsElementsFormat = ( photos ) => {
	const topics = photos.reduce( ( acc, photo ) => {
		return acc.concat( photo.topics );
	}, [] );
	return [ ...new Set( topics ) ].map( ( topic ) => {
		return {
			label: topic
				.replace( /-/g, ' ' )
				.replace( /\b\w/g, ( l ) => l.toUpperCase() ),
			value: topic,
		};
	} );
};

This function leverages the reduce method of Array, built-in Set objects (to get collections of unique values) and regular expressions for the replace method of strings to shape the data to the desired format.

Last, you need to define how the final value for each field will be displayed and used by the DataViews component: 

  1. With the getValue property you can specify a function that determines the value for a field. This value will be used for searching, sorting, and displaying the field. For example:
    • To sort the field by its numeric value, you can convert a string to a number using this function.
    • You can create a composite value by combining multiple properties from the original data. This combined value will be the field’s default display value and will also be used by the search functionality.
  2. With the render property, you can define a custom React component that controls how the field’s value is displayed. This is useful if you want to format the value in a specific way or include additional elements (like icons or buttons) in the display.

Now that you understand better the field settings that can be configured through the fieldsprop, let’s define the array of field items that will be passed on to the Dataviews component.

Add the following code to the src/App.js file:

import { getTopicsElementsFormat } from "./utils";
import { __ } from '@wordpress/i18n';

...


// "fields" definition
const fields = [
	{
		id: 'img_src',
		label: __( 'Image' ),
		render: ( { item } ) => (
			<img alt={ item.alt_description } src={ item.urls.thumb } />
		),
		enableSorting: false,
	},
	{
		id: 'id',
		label: __( 'ID' ),
		enableGlobalSearch: true,
	},
	{
		id: 'author',
		label: __( 'Author' ),
		getValue: ( { item } ) =>
			`${ item.user.first_name } ${ item.user.last_name }`,
		render: ( { item } ) => (
			<a target="_blank" href={ item.user.url } rel="noreferrer">
				{ item.user.first_name } { item.user.last_name }
			</a>
		),
		enableGlobalSearch: true,
	},
	{
		id: 'alt_description',
		label: __( 'Description' ),
		enableGlobalSearch: true,
	},
	{
		id: 'topics',
		label: __( 'Topics' ),
		elements: getTopicsElementsFormat( dataPhotos ),
		render: ( { item } ) => {
			return (
				<div className="topic_photos">
					{ item.topics.map( ( topic ) => (
						<span key={ topic } className="topic_photo_item">
							{ topic.toUpperCase() }
						</span>
					) ) }
				</div>
			);
		},
		filterBy: {
			operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ],
		},
		enableSorting: false,
	},
	{
		id: 'width',
		label: __( 'Width' ),
		getValue: ( { item } ) => parseInt( item.width ),
		enableSorting: true,
	},
	{
		id: 'height',
		label: __( 'Height' ),
		getValue: ( { item } ) => parseInt( item.height ),
		enableSorting: true,
	},
];

It’s a best practice to pass every string intended for display to the end user through an i18n function to ensure proper translation. The code above uses the Javascript i18n method:__

The layouts

The DataViews component needs to know the layouts what will be available for the user. This is set in the defaultLayouts prop.

The three current possible layouts to be displayed with DataViews are:

  • table.
  • grid.
  • list.

If defaultLayouts is empty, Data Views will enable all layout types with empty layout data.

Add the following code to define the defaultLayouts const that we’ll pass to the defaultLayouts prop of the DataViews component to enable the table and grid views:

// "defaultLayouts" definition
const primaryField = 'id';
const mediaField = 'img_src';

const defaultLayouts = {
	table: {
		layout: {
			primaryField,
		},
	},
	grid: {
		layout: {
			primaryField,
			mediaField,
		},
	},
};

List view (a.k.a. “side by side” view) is actually meant to be used as a sidebar view for when there’s a detailed view for each item. You can see an example of this view at Pages view of the Site Editor.

The defaultLayouts prop should be an object that includes properties named table, grid, or list, depending on the layouts you want to enable. Each of these properties should contain a layout property, which holds the configuration for that specific layout type.

The properties used in this project to configure the layouts are:

  • primaryField: The id of the field to be highlighted in each row/card/item. Used by the table, grid and list layouts.
  • mediaField: The id of the field to be used for rendering each card’s media. Used by the grid and list layouts.

There are other properties available for the grid layout, such as badgeFields and columnFields.

The view

The view object represents the state of the Data Views configuration (how the dataset is rendered, which fields are visible…, etc.). It should be initialized with an object and updated whenever the view setting changes. It is the developer’s responsibility to ensure the view value is consistently updated whenever the user modifies view options through the UI. The onChangeView callback is called every time the view settings are changed via the UI, making it the ideal place to update the view state.

The view settings are defined in the form of a serializable object with the  following properties:

  • type : view type, one of table, grid, list.
  • search: the text search applied to the dataset.
  • filters: the filters applied to the dataset in the form of an array of objects with the following properties:
    • field: which field this filter is bound to.
    • operator: which type of filter operator it is.
    • value: the value selected by the user from the ones passed to the ‘elements’ property for that field’s definition.
  • perPage: number of records to show per page.
  • page : the current page that is visible.
  • sort: sort settings for a field:
    • field: the field used for sorting the dataset.
    • direction: the direction to use for sorting, one of ‘asc’ or ‘desc’.
  • fields: the id`s of the fields that are visible in the UI and the specific order in which they are displayed.
  • layout: config that is specific to a particular layout type. Same properties that are used to defined the defaultLayouts can be used here to update the layout config.

Set the following code inside the React app component in src/App.js before the return of the component: 

import { useState } from "@wordpress/element";
...
	// "view" and "setView" definition
	const [ view, setView ] = useState( {
		type: 'table',
		perPage: 10,
		layout: defaultLayouts.table.layout,
		fields: [
			'img_src',
			'id',
			'alt_description',
			'author',
			'topics',
			'width',
			'height',
		],
	} );

The view object needs a default list of fields that will be displayed and the order in which they should appear. If you don’t provide this default value for the fields property of view, features like movings columns to the right or left won’t work.

With the useState React Hook, a view state variable and a setView method to update it, are defined. The setView method can be passed directly as the onChangeView callback so that every time the UI changes, altering the Dataview settings, the view object is directly updated, and the React component is refreshed.

But the data also needs to be updated to reflect the filters, orderings and other settings selected by the user. Uff! This sounds like a lot to handle, right? 

I have good news for you. The @wordpress/dataviews package has us covered for this operation with the filterSortAndPaginate function. This function will apply the filtering, sorting, and pagination to the raw data based on the view configuration, returning the filtered data and the pagination info.

The filterSortAndPaginate takes three arguments:

  • Raw data.
  • View config object.
  • Fields config.

And returns an object with the following properties:

Let’s import this function from the @wordpress/dataviews package and add the following code to the App  React component:

import { ..., filterSortAndPaginate } from "@wordpress/dataviews";
import { ..., useMemo } from "@wordpress/element";
...

	// "processedData" and "paginationInfo" definition
	const { data: processedData, paginationInfo } = useMemo( () => {
		return filterSortAndPaginate( dataPhotos, view, fields );
	}, [ view ] );

In this code, the useMemoReact hook is used to cache the results of filterSortAndPaginate and optimize the computing resources. Every result returned by useMemo is associated with its input values and internally saved, so the second time useMemo is called with the same input values, the returned values will be delivered from the cached results (optimizing calls to filterSortAndPaginate).

Every time the view state variable changes, the useMemo function is called, and it will return the processedData and paginationInfo values, either from its internal cache or from the result of calling filterSortAndPaginate.

With the code above, we’ve established the necessary logic and variables for Data Views to manage the data, taking into account the view settings that the user can modify.

The actions

To finish the part of the project covered in this article, let’s add a simple action that can be launched over each item of the Data Views. The action will open a new window with the original URL of each photo item.

The DataViews component’s actions prop accepts a collection of operations that can be performed on each item.

Add the following code to src/App.js:

// "actions" definition
const actions = [
		{
			id: 'see-original',
			label: __( 'See Original' ),
			callback: ( [ item ] ) => {
				const urlImage = item.urls.raw;
				window.open( urlImage, '_blank' );
			},
		},
	];
  ];

The action has been defined with the following properties:

  • id – Unique identifier of the action.
  • label – User-facing description of the action to be displayed in the UI.
  • callback – Callback function that takes the record as input and performs the required action.

Check here the complete list of properties that can be used to define actions for the Data Views items and enable additional behaviors. 

At this point, your src/App.js should look like this:

import { DataViews, filterSortAndPaginate } from '@wordpress/dataviews';
import { getTopicsElementsFormat } from './utils';
import { useState, useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import './style.scss';

// source "data" definition
import { dataPhotos } from './data';

// "defaultLayouts" definition
const primaryField = 'id';
const mediaField = 'img_src';

const defaultLayouts = {
	table: {
		layout: {
			primaryField,
		},
	},
	grid: {
		layout: {
			primaryField,
			mediaField,
		},
	},
};

// "fields" definition
const fields = [
	{
		id: 'img_src',
		label: __( 'Image' ),
		render: ( { item } ) => (
			<img alt={ item.alt_description } src={ item.urls.thumb } />
		),
		enableSorting: false,
	},
	{
		id: 'id',
		label: __( 'ID' ),
		enableGlobalSearch: true,
	},
	{
		id: 'author',
		label: __( 'Author' ),
		getValue: ( { item } ) =>
			`${ item.user.first_name } ${ item.user.last_name }`,
		render: ( { item } ) => (
			<a target="_blank" href={ item.user.url } rel="noreferrer">
				{ item.user.first_name } { item.user.last_name }
			</a>
		),
		enableGlobalSearch: true,
	},
	{
		id: 'alt_description',
		label: __( 'Description' ),
		enableGlobalSearch: true,
	},
	{
		id: 'topics',
		label: __( 'Topics' ),
		elements: getTopicsElementsFormat( dataPhotos ),
		render: ( { item } ) => {
			return (
				<div className="topic_photos">
					{ item.topics.map( ( topic ) => (
						<span key={ topic } className="topic_photo_item">
							{ topic.toUpperCase() }
						</span>
					) ) }
				</div>
			);
		},
		filterBy: {
			operators: [ 'isAny', 'isNone', 'isAll', 'isNotAll' ],
		},
		enableSorting: false,
	},
	{
		id: 'width',
		label: __( 'Width' ),
		getValue: ( { item } ) => parseInt( item.width ),
		enableSorting: true,
	},
	{
		id: 'height',
		label: __( 'Height' ),
		getValue: ( { item } ) => parseInt( item.height ),
		enableSorting: true,
	},
];
const App = () => {
	// "view" and "setView" definition
	const [ view, setView ] = useState( {
		type: 'table',
		perPage: 10,
		layout: defaultLayouts.table.layout,
		fields: [
			'img_src',
			'id',
			'alt_description',
			'author',
			'topics',
			'width',
			'height',
		],
	} );

	// "processedData" and "paginationInfo" definition
	const { data: processedData, paginationInfo } = useMemo( () => {
		return filterSortAndPaginate( dataPhotos, view, fields );
	}, [ view ] );

	// "actions" definition
	const actions = [
		{
			id: 'see-original',
			label: __( 'See Original' ),
			callback: ( [ item ] ) => {
				const urlImage = item.urls.raw;
				window.open( urlImage, '_blank' );
			},
		},
	];
	return (
		<DataViews
			data={ processedData }
			fields={ fields }
			view={ view }
			onChangeView={ setView }
			defaultLayouts={ defaultLayouts }
			actions={ actions }
			paginationInfo={ paginationInfo }
		/>
	);
};

export default App;

The DataViews component is now ready to be displayed! 

Go to your Admin panel and the Add Media from third party service subpage of the “Media” Settings. You should see something like this:

This layout should reflect all the settings and processes the DataViews component has been called with:

  • A table and grid layout to choose from.
  • Filter by Topic.
  • Some columns to be sorted by.
  • An action to trigger per item.
  • Hide and show columns.
  • Change the position of columns.
  • Data to display and pages are updated according to the settings chosen by the user.

The code of this first part of the project is available here. There’s also a live demo of the project powered by Playground. 

With one component (the DataViews component), you have built an advanced UI from a plugin to display custom data, including several layout types, search, filters, ordering, pagination, and custom actions over the items.

Wrapping up

The @wordpress/dataviews package is playing a significant role in the WP Admin redesign. The @wordpress/dataviews package also has a Dataforms component that aims to simplify and standardize UI creation for the admin by using a declarative approach (you define what you want, and the system creates it) and sharing the same API as the Dataviews component.

Plans for the future to leverage these components to enhance and unify the Admin UI include Quick editing and the Media Library.

Built with extensibility in mind, the @wordpress/dataviews package opens up exciting new opportunities for plugins and third-party developers. To learn more about the planned work on Data Views extensibility, be sure to check out this tracking issue.

Now that you’ve built a project with the DataViews component, it may be easier for you to understand the code used in Core to implement Data Views in the Site Editor for Pages, Patterns, and Templates.

Stay tuned for the second part of this article, where you’ll learn how to take this project further and add images to the Media Library from the Data Views UI.

Resources


Props to @greenshady, @ndiego, @welcher, @bph, @marybaum, @oandregal and @eidolonnight for providing feedback and reviewing this post.

7 responses to “Using Data Views to display and interact with data in plugins”

  1. Huan Avatar

    Thank you JuanMa. As a plugin developer, I’m most interested in topics of extending core blocks and Data Views.

    I find one strange thing about WP developer blog: Almost no author sets the code block to “wide” width, so readers have to painfully scroll horizontally each time they to view the code. Hopefully the Wide width feature can be used more here.

    1. Huan Avatar

      I added `javascript: (function () { document.querySelectorAll(‘pre.wp-block-code’).forEach(function (el) { el.classList.add(‘alignwide’); }); })();` to Chrome bookmark and problem solved. 🙂

    2. JuanMa Garrido Avatar

      Thank you for your comment, Huan.

      The font size of code blocks has been decreased to reduce the need for horizontal scrolling. Hope this change improves the readability of posts.

      Using a wide width for each code block can disrupt the reading flow, so most authors prefer to keep them in line with the rest of the content.

  2. codersantosh Avatar

    Hi there! I’m currently using custom components for this feature, but it would be great to leverage core features instead. I’m curious about how dynamic data handling, such as API calls for search, pagination, sorting, filtering, etc, would work. An example or a follow-up tutorial on this would be beneficial. Thanks!

  3. Per Søderlind Avatar

    “this first part of the project”, does it mean you have plans for more good stuff 🙂

  4. paul Avatar
    paul

    Can Dataviews be used in a custom block?

Leave a Reply

Your email address will not be published. Required fields are marked *