How to add a gradient text effect to specific words in a block

By Ian Svoboda on December 17, 2025

Gradient text effects are pretty nifty and can add a nice element of visual flair to your posts (if I do say so myself). Adding this effect is pretty easy with CSS, but what if you want to only apply it to certain words inside a paragraph, heading, or similar block? In this post I’ll show you how you can add a gradient text effect that you can use for any block that accepts text content.

WordPress blocks have a concept for “text formats” to style parts of the text inside a given block. The most common examples would be things like bold and italic text but there are a number of text formats built-in to WordPress that text-based blocks can use including:

  • Superscript
  • Subscript
  • Highlights (apply a specific color)
  • Footnotes
  • and more!

Here’s an example of what this looks like in the editor:

Screenshot of the block toolbar showing a dropdown menu full of registered block formats.
The above screenshot shows the current text formats available in this site at the time of writing.

When I was building this site, I was interested in setting up gradient text to work in the same manner as a highlight. I already use this styling on the “dek” or intro to my posts, but I thought it would be cool to choose random words in a given sentence or headline and apply that gradient text effect to them.

We’ll start by explaining the CSS required and then dig into how to implement the new text format type.

Creating a gradient text effect with CSS

There are only a few properties required to create this effect: background-clip, background-image (for the gradient), and color.

Here’s a complete example that I’m using on this site:

CSS
.has-gradient-text,
.is-style-dek {
    background-clip: text;
    background-image: linear-gradient(to right, var(--theme-color-primary), var(--theme-color-secondary));
    color: transparent;
}
CSS

The thing that makes this all work is background-clip: text which confines the background to the text inside the element. From there, we’re using background-image to set the gradient and then we set the text color to transparent to allow the gradient to be visible.

So the exact colors will vary depending on your theme but otherwise these are the only CSS styles we’ll need to create the effect.

Defining the text format

Next we need to register our new text format and create a simple control for the block editor UI. Both of these things can easily be done inside of a single JavaScript file.

Note: As a general rule, I’d recommend you have a single file per text format you’re adding. It keeps all of the relevant code together and makes it super easy to copy/paste to new projects.

Before you begin, you’ll need to make sure you’re using a build process. My recommendation is to use @wordpress/scripts or 10up-toolkit to make this really easy.

To get started, create a file in your theme or plugin called gradient-text.js. If you’re using @wordpress/scripts or 10up-toolkit to build your JavaScript files, you should place this in whatever folder makes sense with your project setup. In my case, there’s a folder called “block-formats” where I have this file located in my theme.

There are two things we need to do in this file:

  1. Define our toolbar edit component so we can select the text format in the editor
  2. Register the text format with WordPress.

Importing necessary functions

Our next step is importing a few functions to use in our React component

gradient-text.js
import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
import { RichTextToolbarButton } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
JavaScript
  • registerFormatType: This is used to register the format with WordPress and allow our format to appear in the editor
  • toggleFormat: Used to handle changing the UI and updating the content when our format is enabled or disabled
  • RichTextToolbarButton: This React component is used to display buttons in the Block Toolbar.
  • useSelect: This function is used to get the state or settings for the editor.
  • __: This translation function allows our text labels to be translatable. It’s optional, but highly recommended to ensure the text you enter is able to be translated (even if it isn’t right now)1.

These functions will be used in our toolbar component in the editor (i.e. the thing that you click on to select the format).

Creating the toolbar component

So our next step is to create a React function component that uses these functions to output a button in the Block Toolbar for us to use while editing posts and pages. This component will receive a few props automatically when we go to register the toolbar button:

  • isActive: determines if the text format is active or not
  • onChange: What should happen when someone changes (i.e. enables or disables) the text format
  • value: The text the format should apply to

Props for React components come through as a single object, so we’re using destructuring to get specific keys out of that object to use in our component. This is a very common pattern in React and modern JavaScript in general.

gradient-text.js
import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
import { RichTextToolbarButton } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

function GradientTextControl ({ isActive, onChange, value }) {
  // 1. Get the current selected block.
	const selectedBlock = useSelect((select) => {
		return select('core/block-editor').getSelectedBlock();
	}, []);

  // 2. Determine which blocks this format should be available for.
	const allowedBlocks = ['core/paragraph', 'core/heading'];

  // 3. Check if there is a block selected and if it's one of the allowed blocks.
	if (selectedBlock && !allowedBlocks.includes(selectedBlock.name)) {
		return null;
	}

  // 4. Output the button for the Block Toolbar that appears inside the dropdown menu.
	return (
		<RichTextToolbarButton
			icon="editor-code"
			title={ __( 'Gradient Text', 'lwptd' ) }
			onClick={() => {
				onChange(
					toggleFormat(value, {
						type: 'lwptd/gradient-text',
					}),
				);
			}}
			isActive={isActive}
		/>
	);
};
JavaScript

Let’s break down what’s happening above:

  1. We ask the editor to give us the currently selected block (if any). If no block is selected, the Block Toolbar won’t display so there’s nothing else to do.
    • The selectedBlock variable will be null if no block is selected, or an object if one is selected.
  2. Here we’ve provided an array of registered blocks that should be able to use this format. If you didn’t include this and the conditional check below, the format would be available for any block, which is almost never what you want.
    • Some blocks don’t use text formats or otherwise won’t work as expected if you tried to use this. In general, you should only apply things like this to specific blocks to avoid side-effects or unexpected behavior.
    • The block names you add to the array should include the namespace as well as the block’s slug, as in the above example (namespace/slug).
  3. Now we check to see if there is a selected block and if so, if the block’s name is included in the list of allowed blocks. If not, then we return null which instructs React to not output anything for this component.
  4. From here we output a RichTextToolbarButton component with the props we need:
    • icon: This is the icon that displays next to the button text in the Block Toolbar. You can use a dashicon name (like the editor-code) or you can provide your own SVG icon instead.
    • title: The text for the toolbar button. This will display after the icon. Make sure you update the text domain from lwptd to whatever your theme or plugin uses.
    • onClick: What happens when someone clicks on this button.
      • We’re passing an anonymous function that calls onChange from our component’s props. We then pass the toggleFormat function we imported previously as a parameter for the onChange and provide it the props it needs to toggle the format: the value prop and an object with the key type to indicate which format type is being toggled.
    • isActive: We pass the isActive prop from our component into this component. If active, the button’s icon will have inverted colors (white on black) and the dropdown menu icon will be inverted to show you something is active inside of it.

The output of the above code will look like this:

Screenshot of the new text format after being added to the block toolbar

Here’s how this will look when it’s active:

Screenshot of the new text format in the block toolbar when the format is active

Adding the button outside of the dropdown (optional)

You may also add the format button in the editor toolbar directly without placing it inside the dropdown. The code is ultimately very similar and just requires you to use a few additional components and make a few key changes:

  • Use the ToolbarButton component instead of RichTextToolbarButton
  • Output the button inside of ToolbarGroup and BlockControls

Here’s an example of how this would look with our current setup:

gradient-text.js
import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

function GradientTextControl ({ isActive, onChange, value }) {
	const selectedBlock = useSelect((select) => {
		return select('core/block-editor').getSelectedBlock();
	}, []);

	const allowedBlocks = ['core/paragraph', 'core/heading'];

	if (selectedBlock && !allowedBlocks.includes(selectedBlock.name)) {
		return null;
	}

	return (
	  <BlockControls>
      <ToolbarGroup>
    		<ToolbarButton
    			icon="editor-code"
    			title={ __( 'Gradient Text', 'lwptd' ) }
    			onClick={() => {
    				onChange(
    					toggleFormat(value, {
    						type: 'lwptd/gradient-text',
    					}),
    				);
    			}}
    			isActive={isActive}
    		/>
      </ToolbarGroup>
		</BlockControls>
	);
};
JavaScript

Here’s how that would then appear in the toolbar:

Screenshot of the block toolbar showing the new format as a button outside the dropdown and next to the bold icon.

Registering the new format

Now that we have our toolbar component, we just need to call registerFormatType to tell WordPress what to do with it.

gradient-text.js
import { registerFormatType, toggleFormat } from '@wordpress/rich-text';
import { RichTextToolbarButton } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

function GradientTextControl ({ isActive, onChange, value }) {
	const selectedBlock = useSelect((select) => {
		return select('core/block-editor').getSelectedBlock();
	}, []);

	const allowedBlocks = ['core/paragraph', 'core/heading'];

	if (selectedBlock && !allowedBlocks.includes(selectedBlock.name)) {
		return null;
	}

	return (
		<RichTextToolbarButton
			icon="editor-code"
			title={ __( 'Gradient Text', 'lwptd' ) }
			onClick={() => {
				onChange(
					toggleFormat(value, {
						type: 'lwptd/gradient-text',
					}),
				);
			}}
			isActive={isActive}
		/>
	);
};

registerFormatType('lwptd/gradient-text', {
	title: __( 'Gradient Text', 'lwptd' ),
	tagName: 'span',
	className: 'has-gradient-text',
	edit: GradientTextControl,
});
JavaScript

When you call registerFormatType you need to pass it two total parameters: the format name (including namespace) and the settings object. The settings object above includes a few key things:

  • title: The title of the text format. Should likely be the same as the button text.
  • tagName: When you add a text format, the selected text is wrapped in an HTML element. Here you can specify which element to use.
    • When you’re adding a format for inline text (i.e. text that sits next to other text) you want to use an element that displays inline by default (such as a span, i, strong, em, or similar.
  • className: The HTML class that should be added to the tag.
  • edit: The toolbar button component that displays in the editor. In our case, we’re using the function we defined earlier in the file.

Adding the block format to the editor

Now, we need to make sure that the JavaScript we just wrote makes its way into the editor. If you’re already bundling your JavaScript and extending other things in the block editor or creating your own blocks, you probably have a file that is loading in the editor for this purpose. If so, you just need to make sure this file is imported or otherwise bundled within that file.

If you don’t have such a file prepared already and are setting all of this up for the first time, you may want to set up a single entry file (i.e. a file output by your build tool that may be bundled with other files) which you can enqueue in the block editor.

Creating a new entry

I’ve found that it’s nice to have a single file for this kind of thing that imports other individual files. Here’s an example for how this might look:

- src/
  - js/
    - block-formats/
      - gradient-text.js
      - index.js
    - core-block-overrides.js
Plaintext

Inside the src/js folder, you’ll have a folder called block-formats where all block formats you create will be defined inside their own file. That folder then has an index.js file (aka a “barrel file”) that will import each individual file inside of it:

src/js/block-formats/index.js
import './gradient-text';
JavaScript

Then the new core-block-overrides.js file imports that one index file:

src/js/core-block-overrides.js
import './block-formats'; // Imports block-formats/index.js
JavaScript

So now any block format that is imported into src/js/block-formats/index.js will automatically be imported into this file. This is a really nice pattern that is especially relevant if you’re importing multiple types of overrides or extensions such as block variations, block styles, or other hooks to change things in the editor. Here’s how that might look with some other things in it to give you some context:

src/js/core-block-overrides.js
import './block-styles';
import './block-variations';
import './block-formats';
JavaScript

This is a professional pattern used on many big projects but you don’t explicitly have to do it this way. As long as you’re able to import the block format you create into the file, you’re all set!

If you’re making a new entry file, you’ll need to tell webpack (or whatever you’re using) to create this file. You can refer to the documentation for @wordpress/scripts, 10up-toolkit, or whatever build tool you’re using for how to do this (there’s a few different ways per tool but it’s pretty straightforward).

The key is to make sure you’re importing these things into a file that only displays in the editor. There’s no purpose or benefit to loading this on the frontend and it will cause additional scripts to appear on the frontend for no reason which will cause your site to be slower.

Once you confirm your entry file is being built, the next step is make sure it loads in the block editor.

Adding your script to the block editor

Now that you have your entry file ready, you’ll need to enqueue it to appear in the block editor. There is a specific hook that you need to use for this purpose: enqueue_block_editor_assets.

This is not the same hook you’d use if you were including styles or scripts meant to appear inside the editor iframe. This hook is used to enqueue assets for the editor UI itself (such as the block toolbar).

Here’s a simple example of what this enqueue would look like:

PHP
<?php

add_action( 'enqueue_block_editor_assets', function() {
  // Get the core-block-overrides.asset.php file generated by webpack.
  $asset_info = get_asset_info('core-block-overrides');
  
  wp_enqueue_script( 
    'core-block-overrides',
    get_theme_file_uri( 'src/js/core-block-overrides.js' ), 
    $asset_info['dependencies'],
    $asset_info['version'],
    true
  );
} );
PHP
About get_asset_info

This function is a custom one I created for my theme to dynamically fetch the version and script dependencies that a given entry may need to run. This is necessary to ensure the various @wordpress/* packages you’re importing or other scripts included by WordPress are properly enqueued along with your file.

If you’re not familiar with this, check out the relevant section in the post I wrote recently.

Once you’ve added the necessary PHP to your plugin or theme, check out the editor UI and you should see your new block format in the toolbar!

Conclusion

Now you know how to add a block text format you can easily add new ones for things besides gradient text. As long as you can style it with CSS and a single HTML element, it be should straight forward from here to add any other formats you wish by creating a new file in the same folder as the gradient-text.js file and then importing it into that index.js file you created.

Happy coding!

References

  1. When you’re doing professional WordPress work it’s advisable to make everything translatable by default. This makes it much easier to support translations in the future and doesn’t require much effort up front. ↩︎


Leave a Reply

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