Skip to main content
Blocks can generate front-end output in two ways: static rendering (fixed HTML saved to the database) or dynamic rendering (server-generated HTML on each request).
Read Static vs. dynamic blocks: What’s the difference? for a great introduction to this topic.

Static rendering

Blocks with static rendering produce fixed HTML that is stored in the database when saved. This HTML remains unchanged unless manually edited in the Block Editor.

How static rendering works

1

User edits block

Content is modified in the Block Editor
2

Save function executes

The save function generates HTML markup
3

HTML stored in database

Generated markup is saved within block delimiters
4

Front-end display

Stored HTML is retrieved and displayed on page load

Defining static rendering

The save function specifies the HTML structure saved to the database:
import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function save( { attributes } ) {
    return (
        <pre { ...useBlockProps.save() }>
            <RichText.Content value={ attributes.content } />
        </pre>
    );
}

Saved markup format

Blocks are stored with HTML comment delimiters:
<!-- wp:preformatted -->
<pre class="wp-block-preformatted">This is some preformatted text</pre>
<!-- /wp:preformatted -->
On the front end, only the inner HTML is displayed:
<pre class="wp-block-preformatted">This is some preformatted text</pre>

When to use static rendering

✅ Content that doesn’t need to update automatically
✅ Simple blocks with fixed structure
✅ Blocks where performance is critical
✅ Content that should remain stable even if plugin is deactivated

Examples of static blocks

Dynamic rendering

Blocks with dynamic rendering generate content server-side upon each request. The database stores block attributes but not the full HTML output.

How dynamic rendering works

1

User edits block

Attributes are modified in the Block Editor
2

Attributes stored

Only block attributes saved to database
3

Page requested

Front-end page is loaded
4

Server renders block

PHP render callback generates HTML dynamically

When to use dynamic rendering

Use dynamic blocks when:
  1. Content should update automatically: Latest Posts block updates when new posts are published
  2. Markup needs to change globally: Updating block structure automatically applies to all instances
  3. External data required: Fetching data from APIs or database queries
  4. User-specific content: Displaying personalized content based on user state
Changing the save function of an existing static block can cause validation errors. Dynamic rendering avoids this issue.

Defining dynamic rendering

Dynamic blocks can use either:

1. Render callback in PHP

register_block_type( 'my-plugin/dynamic-block', array(
    'render_callback' => 'render_dynamic_block',
    'attributes' => array(
        'postId' => array(
            'type' => 'number',
        ),
    ),
) );

function render_dynamic_block( $attributes, $content, $block ) {
    $wrapper_attributes = get_block_wrapper_attributes();
    $post = get_post( $attributes['postId'] ?? 0 );
    
    if ( ! $post ) {
        return '';
    }
    
    return sprintf(
        '<div %1$s><h2>%2$s</h2><div>%3$s</div></div>',
        $wrapper_attributes,
        esc_html( $post->post_title ),
        wp_kses_post( $post->post_excerpt )
    );
}

2. Render template file (render.php)

In block.json:
{
  "render": "file:./render.php"
}
In render.php:
<?php
$wrapper_attributes = get_block_wrapper_attributes();
$post = get_post( $attributes['postId'] ?? 0 );

if ( ! $post ) {
    return;
}
?>
<div <?php echo $wrapper_attributes; ?>>
    <h2><?php echo esc_html( $post->post_title ); ?></h2>
    <div><?php echo wp_kses_post( $post->post_excerpt ); ?></div>
</div>

Render function parameters

Both methods receive three parameters:
$attributes
array
The array of block attributes
$content
string
The markup saved in the database (from save function if defined)
$block
WP_Block
The instance of the WP_Block class with block metadata

Dynamic block with null save

For purely dynamic blocks, return null from the save function:
import { useBlockProps } from '@wordpress/block-editor';

// Edit component
export function Edit( { attributes, setAttributes } ) {
    return (
        <div { ...useBlockProps() }>
            {/* Editor UI */}
        </div>
    );
}

// Save function returns null for dynamic blocks
export function save() {
    return null;
}

Examples of dynamic blocks

Hybrid approach

Blocks can define both a save function and dynamic rendering. The dynamic rendering takes precedence, but the saved HTML serves as a fallback:
// Save function provides fallback
export function save( { attributes } ) {
    return (
        <div { ...useBlockProps.save() }>
            Fallback content if render callback unavailable
        </div>
    );
}
// Render callback used when available
function render_block( $attributes, $content, $block ) {
    // Dynamic rendering logic
    return '<div>Dynamic content</div>';
}
This approach is useful for maintaining content if the block’s plugin is deactivated.

Server-side rendering in the editor

For dynamic blocks, preview the server-rendered output in the editor using <ServerSideRender>:
import { useBlockProps } from '@wordpress/block-editor';
import ServerSideRender from '@wordpress/server-side-render';

export default function Edit( { attributes } ) {
    return (
        <div { ...useBlockProps() }>
            <ServerSideRender
                block="my-plugin/dynamic-block"
                attributes={ attributes }
            />
        </div>
    );
}
ServerSideRender makes an API request on every attribute change. For better performance, build a custom Edit component that mirrors the server output.

Comparison

FeatureStatic RenderingDynamic Rendering
HTML StorageFull HTML in databaseAttributes only
PerformanceFaster (no server processing)Slower (server execution)
UpdatesManual editing requiredAutomatic updates
Markup ChangesCan cause validation errorsNo validation issues
Use CasesSimple, fixed contentData-driven, updating content
ExamplesParagraph, Image, ButtonLatest Posts, Site Title

Next steps

Dynamic blocks

Deep dive into creating dynamic blocks

JavaScript in editor

Learn about the JavaScript build process

Build docs developers (and LLMs) love