Skip to content

Gutenberg content – fluent-wp-client

Import fluent-wp-client/blocks when you need Gutenberg-aware workflows. The core client stays lightweight.

Four workflows:

  • rendered HTML via content.rendered
  • raw content via .getContent()
  • block type discovery via wpBlocks.blocks()
  • block read/write via blocks().get() and blocks().set()
import { WordPressClient } from 'fluent-wp-client';
import { withBlocks } from 'fluent-wp-client/blocks';
const wp = new WordPressClient({
baseUrl: 'https://example.com',
auth: { username: 'admin', password: 'app-password' },
});
const wpBlocks = withBlocks(wp);
const availableBlocks = await wpBlocks.blocks().list();
const blockSchemas = await wpBlocks.blocks().schemas();
const paragraph = await wpBlocks.blocks().item('core/paragraph');
const paragraphSchema = await wpBlocks.blocks().schema('core/paragraph');
const post = await wp.content('posts').item('hello-world');
const html = post.content.rendered;
const content = await wp.content('posts').item('hello-world').getContent();
content.rendered // HTML
content.raw // block markup
const blocks = await wpBlocks.content('posts').item('hello-world').blocks().get();
// WordPressParsedBlock[] | undefined (undefined if item not found)
const blockSchemas = await wpBlocks.blocks().schemas();
const blocks = await wpBlocks.content('posts').item('hello-world').blocks().get({
schemas: blockSchemas,
validate: true,
});

Parsed blocks contain blockName, attrs, innerBlocks, innerHTML, innerContent. Whitespace-only freeform nodes are filtered out automatically.

const postQuery = wpBlocks.content('posts').item('hello-world');
const blocks = await postQuery.blocks().get();
const updated = [
{ ...blocks[0]!, innerHTML: '<p>New text.</p>', innerContent: ['<p>New text.</p>'] },
...blocks.slice(1),
];
await postQuery.blocks().set(updated);
PatternWhat it does
set(blocks)Structural + round-trip checks only
set(blocks, blockSchemas)Full catalog validation (names + attributes)
set(blocks, await wpBlocks.blocks().schemas({ namespace: 'core' }))Namespace-scoped validation
set(blocks, [singleSchema])Single block type validation
set(blocks, { validate: false })Skip all validation

set() checks:

  • innerContent placeholders match innerBlocks
  • block names and attributes match provided schemas
  • parent/ancestor constraints
  • serialization round-trips cleanly

List methods return plain DTOs. Use a single-item query for block access:

const posts = await wp.content('posts').list({ perPage: 5 });
const blocks = await wpBlocks.content('posts').item(posts[0].id).blocks().get();

When you already have content.raw:

import {
createWordPressBlockJsonSchemas,
parseWordPressBlocks,
serializeWordPressBlocks,
validateWordPressBlocks,
} from 'fluent-wp-client/blocks';
const blocks = await parseWordPressBlocks(rawContent);
const serialized = serializeWordPressBlocks(blocks);
import { parsedBlockSchema } from 'fluent-wp-client/blocks/zod';
const blockTree = z.array(parsedBlockSchema).parse(aiOutput);
  • .item() — context=view, public access
  • .getContent(), .blocks().get(), .blocks().set() — context=edit, requires edit capabilities
  • wpBlocks.blocks() — requires authenticated user with block type management access
  • Missing auth/capabilities throw WordPressHttpError or WordPressAuthError
  • TypeScript92%
  • PHP6%
  • JavaScript2%