Skip to main content

Using @wordpress packages on the frontend

As part of the Gutenberg project WordPress has gained much more than just the editor itself. The Gutenberg repository currently houses more than 80 individual packages. These packages span everything from the actual React components, utilities to calculate word count, end-to-end test utilities and much more. Naturally there is the desire to also use some of these packages in the frontend code we are shipping. However, because there are many caveats when trying to use them on the frontend which is why it is generally not recommended to do so.

You can find a list of @wordpress/ packages that are the exception to this rule and that can be used in the Useful packages outside of the editor section.

caution

The @wordpress/ dependencies are first and foremost designed to be used within the editor. Therefore they are not necessarily optimized for frontend performance and size. Some packages rely on lodash or moment and therefore come with a lot of code.

Starting in WordPress 6.1 a lot of packages have dropped their reliance of lodash.

Bundle size

One of the pitfalls of using the Dependency Extraction Webpack Plugin is that you don't see the size of the externalized WordPress packages. They are not a part of your bundle but instead get added as an additional script that gets loaded before yours. Given these packages are bundled via WodPress, they don't allow you to do any sort of tree shaking.

This is especially problematic because they often rely on individual functions from lodash but therefore load all of lodash as a result which is a heavy import.

Speaking of lodash one pitfall is, that the Dependency Extraction Webpack Plugin externalizes more than just the @wordpress/* dependencies. It externalizes all these imports:

info

There are some @wordpress/ packages, like the @wordpress/icons package, that are not bundled in WordPress and therefore don't get externalized. You can view the excluded list in the GitHub repo.

This means that even if any of your other frontend dependencies try to load something from lodash the Dependency Extraction Webpack Plugin will pick that up and add lodash to your dependency array.

Editor dependant packages

The @wordpress/packages can also be divided into different groups. There are some that are dependant on being used in the editor. The entire @wordpress/block-editor package for example should not be used outside of the editor because it depends on the surrounding architecture like the data api being setup correctly etc.

caution

As a rule of thumb any package that includes editor in it's name should not be used outside of the editor.

Useful packages outside of the editor

There are some packages that suit themselves very well for being used outside of the editor. This list is not comprehensive and if something is not listed here it doesn't mean that it cannot be used on the frontend. These are just some good examples of packages that showed they work well on the frontend.

@wordpress/api-fetch

The @wordpress/api-fetch package is great for making it easier to talk to the WordPress REST API from your frontend code. It allows you to configure middlewares so, for example, you could define the root URL of your project to define it throughout the entire frontend bundle.

import apiFetch from '@wordpress/api-fetch';
const rootURL = 'http://my-wordpress-site/wp-json/';
apiFetch.use( apiFetch.createRootURLMiddleware( rootURL ) );

To make the above even better we can use wp_localize_script to pass the rest_base value to the frontend code as a variable:

wp_localize_script(
'frontend',
'tenupTheme',
[
'restBase' => get_rest_url(),
]
);
import apiFetch from '@wordpress/api-fetch';
const rootURL = window.tenupTheme.restBase;
apiFetch.use( apiFetch.createRootURLMiddleware( rootURL ) );

Or if you need to deal with authenticated requests, you can also create a middleware to work with nonces:

import apiFetch from '@wordpress/api-fetch';
const nonce = 'nonce value';
apiFetch.use( apiFetch.createNonceMiddleware( nonce ) );

@wordpress/dom-ready

The @wordpress/dom-ready package is a simple utility function that makes it super simple to only invoke a callback once the dom is loaded.

import domReady from '@wordpress/dom-ready';

domReady( function () {
//do something after DOM loads.
} );

If you look at the source code for the package it really is nothing more than an event listener for the DOMContentLoaded event with additional checks for the document.readyState complete or interactive.

@wordpress/hooks

The @wordpress/hooks package is a lightweight Event Manager for JavaScript. The API is meant to be as close as possible to the WordPress php hooks API. It is a great way to add extensibility to your JavaScript code.

This package lets you add filters (to be able to change the value of a variable) and actions (to be able to run some code) to your JavaScript code.

@wordpress/html-entities

The @wordpress/html-entities is super useful when working with data from the WordPress REST API. You might want to decode HTML entities to get the final rendered value. This is specially handy if you need to use innerHTML or dangerouslySetInnerHTML if you are using React.

import { decodeEntities } from '@wordpress/html-entities';

const result = decodeEntities( 'á' );
console.log( result ); // result will be "á"

@wordpress/i18n

Localizing strings within frontend js code always is a bit of a pain. Usually you would use wp_localize_script in order to provide the localized strings as a variable. This gets rather difficult to manage though with plurals etc. Using the @wordpress/i18n package to manage frontend translations can solve this by providing the developers with the same __, _n, _x, etc. functions that they are used to from php.

@wordpress/url

This package is a collection of utility functions for working with URLs. It is a great way to manipulate URLs, extract information or even validate them in JavaScript.

The current list of utilities is:

  • addQueryArgs
  • buildQueryString
  • cleanForSlug
  • filterURLForDisplay
  • getAuthority
  • getFilename
  • getFragment
  • getPath
  • getPathAndQueryString
  • getProtocol
  • getQueryArg
  • getQueryArgs
  • getQueryString
  • hasQueryArg
  • isEmail
  • isURL
  • isValidAuthority
  • isValidFragment
  • isValidPath
  • isValidProtocol
  • isValidQueryString
  • normalizePath
  • prependHTTP
  • removeQueryArgs
  • safeDecodeURI
  • safeDecodeURIComponent

We find the functions related to query arguments to be the most useful!