Managing Site Settings from the WordPress Block Editor

Aug 2022 Eric Phillips
Back to blog

If you’ve created blocks for the WordPress block editor ‘Gutenberg’ you might be familiar with how those blocks store data as attributes and how to pass that data around to descendent blocks as context. Maybe you’ve even looked at how to save data from blocks as post meta. What about site-wide settings like the title and logo? We’re accustomed to managing those kinds of settings and theme modifications from the Customizer, but the Customizer we know and love is due to be replaced some time in the near future by theme.json and Full Site Editing.

 

We can catch a glimpse of how the management of those settings which are currently in the realm of the Customizer are likely to be handled under Full Site Editing by taking a peek at core blocks that are already doing it; namely the Site Title and Site Logo/Icon blocks. Major kudos to my colleague Jonathan for pointing me in this direction.

 

Site Title and Site Logo: Case Studies In Customization

We can piece together how to access and change global site options by looking under the hood at the core blocks that already let us control them.

For example, here’s how the site title block gets and sets its content with an entity provider hook:

 

// block-library/src/site-title/edit/index.js

import { useEntityProp } from '@wordpress/core-data';

 

const [ title, setTitle ] = useEntityProp( 'root', 'site', 'title' );


 

And here’s how the site logo block saves the value it gets from its media upload control using a new hook from the data package:

 

// block-library/src/site-logo/edit.js

import { useDispatch } from '@wordpress/data';

import { store as coreStore } from '@wordpress/core-data';

 

const { editEntityRecord } = useDispatch( coreStore );

 

const setLogo = ( newValue ) => {

    editEntityRecord( 'root', 'site', undefined, {

        site_logo: newValue,

    } );

};


But hold on… what? Core store? Entity?

 

State Management, the WordPress Way™️

The core store is modeled after (and built on top of) the Redux API with a few key differences, and it feels like only the core editor team and a handful of tight-lipped wizards know how to actually use it. It includes a number of Entities organized by Kind which can be accessed and managed using a handful of React hooks provided by the core data JavaScript package. The documentation for the WordPress state management system helpfully points to the Redux Glossary, which is useful for understanding what exactly we’re dealing with. It’s by interactions with entities in this system that we can read and change the state of our WordPress website.

To start, let’s take a closer look at that ‘site’ entity we’ve seen the core blocks playing around with:

 

// core-data/src/entities.js

 

export const defaultEntities = [

    // ...

    {

        label: __( 'Site' ),

        name: 'site',

        kind: 'root',

        baseURL: '/wp/v2/settings',

        getTitle: ( record ) => {

            return get( record, [ 'title' ], __( 'Site Title' ) );

        },

    },

    // ...

]

 

A key detail that sticks out is the ‘baseURL’ property, which implies that the properties for this entity are mapped and managed by the settings endpoint of the WordPress REST API.

 

When I first began playing with the core store I had the impression that I was able to add arbitrary fields to the site entity, but they just wouldn’t persist! When I looked at how the site entity was actually defined, it made sense why: I couldn’t submit data the settings REST controller didn’t want or know how to deal with. And the more I thought about it, the more I realized that it’s a very good thing that a REST controller is standing between the block editor and someone putting unvalidated and unsanitized data into my options table 😬

 

The Fast Track To Managing Plugin Settings In FSE

With all this knowledge in hand we can chart a path towards adding our own site option that we can read and change from the block editor.


The first step is letting an entity in the core store know about our setting. To do this, you could register your own REST endpoint and add your own entity to the core store with the endpoint configured as its base URL. A shortcut is registering a setting that shows in REST, which then appears in the response from the WordPress settings controller:

 

<?php

// @see: https://developer.wordpress.org/reference/functions/register_setting/

function lapero_register_settings() {

    register_setting(

        'general',

        'lapero_example_setting',

        array(

            'type'              => 'string',

            'show_in_rest'      => true,

            'sanitize_callback' => 'sanitize_text_field',

        )

    );

}

add_action( 'admin_init',    'lapero_register_settings' );

add_action( 'rest_api_init', 'lapero_register_settings' );

 

 

And now, from the block editor, we can use the same hooks to access and update our setting from the site entity:

 

import { store as coreStore, useEntityProp } from "@wordpress/core-data";

import { useDispatch } from '@wordpress/data';

 

const [exampleSetting, setExampleSetting] = useEntityProp(

    "root",

    "site",

    "lapero_example_setting"

);

 

const { saveEditedEntityRecord } = useDispatch(coreStore);

const onSettingChanged = (value) => {

    setExampleSetting(value);

    saveEditedEntityRecord("root", "site", undefined, {

        lapero_example_setting: value,

    });

};

 

The connections between REST controllers, entities in the store, and the hooks that let us easily interact with those entities is a side of working with the block editor that is not well documented at the moment. I hope that changes as the future of Full Site Editing becomes more clear and the conventions for managing the state of a WordPress website become more concrete. In the meantime, curious WordPress developers should feel empowered to look at the source code of core implementations of features for guidance on how to implement similar functionality in their themes and plugins. And of course, when it finally clicks, share what they learned with the rest of the community 😉