PRC Block Library
The center piece of the PRC Platform. This library includes 85+ custom Gutenberg blocks and opinionated modifications of core blocks, providing a comprehensive set of tools to streamline content creation and ensure consistency across PRC projects. Use this library to create beautiful, responsive content that engages and informs your audience
Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/pewresearch/prc-block-library/archive/refs/heads/trunk.zipThe core Gutenberg block library for the PRC Platform — provides all custom blocks and core block enhancements used across pewresearch.org.
Overview
PRC Block Library registers ~90 blocks across two namespaces: prc-block/* for custom PRC blocks and modifications to core/* blocks. It is the display layer of the platform and the first plugin loaded when rendering any content. Beyond blocks, it also ships a set of cross-cutting support systems (Interactivity API helpers, a Print Engine, block visibility controls, custom rich text formats, sticky/legacy-max-width layout supports, and a form data store) that apply globally to all registered blocks.
In the block editor, the plugin registers the Pew Research Center Block Library block collection (namespace prc-block) in includes/supports/src/index.jsx, which is enqueued with the Supports subsystem.
Dependencies
- Upstream:
prc-platform-core(required plugin),prc-schema-seo(optional, for PDF contact resolution),prc-staff-bylines(optional, for PDF bylines), Report Package plugin (optional, for multi-chapter PDF rendering), WordPress AI plugin (optional, for tabular data AI experiment) - Downstream: Every plugin or theme that renders blocks on the front end depends on this library. Form input blocks (
prc-block/form-input-*) live here; the form container and related blocks live in@prc/block-forms. Theprc-block-library/forms@wordpress/datastore (registered by@prc/block-forms) is a direct integration point for any plugin registering custom form types.
Local Development Setup
Prerequisites
- Node.js 22+ / npm 10.9+
- PHP 8.2+
- WordPress Playground (via `
npm run vip:startfrom repo root)
Running Locally
# Build the entire library (run from repo root)
npm run build:library -w @prc/block-library
# Build a single deprecated block only
npm run build deprecated:grid-controller -w @prc/block-library
# Watch mode for a single deprecated block during development
npm run start deprecated:grid-controller -w @prc/block-library
# Watch mode for the full library
npm run start:library -w @prc/block-library
Running Tests
# Run Playwright e2e tests (from monorepo root; VIP dev-env + Playwright are centralized)
npm run vip:start
npm test -- tests/prc-block-library/e2e/
Architecture
Blocks are discovered and loaded automatically at runtime. Plugin::load_blocks() globs all subdirectories of src/ (in local/dev) or build/ (in production) and calls include_block() for each. Any directory prefixed with _ or . is skipped. Each block is expected to have a class-{block-name}.php file which self-registers with WordPress using register_block_type_from_metadata(). Block metadata is pre-indexed at build time via build/blocks-manifest.php and registered with wp_register_block_metadata_collection() for performance.
Blocks are split into two groups initialized by Plugin:
- Core blocks (
define_core_blocks): extensions and modifications to existingcore/*blocks — adding attributes, context, controls, or custom rendering. - PRC blocks (
define_prc_blocks): net-new blocks in theprc-blocknamespace.
In addition to block classes, Plugin boots several support subsystems via define_library_dependencies(). These run independently of individual blocks and hook into WordPress globally.
Block scaffolding uses @wordpress/create-block with templates located at the repository root (/block-templates). There are four variants:
| Variant | When to use |
|---|---|
default |
Standard block with InnerBlocks and server-side rendering via render.php |
syncedEntity |
Block that wraps a custom post type or synced entity with create/search UI |
contextProvider |
Block that provides context to children and uses Interactivity API routing |
coreBlock |
Extension of an existing core/* block via filters |
To scaffold a new block, run npm run create-block from the repository root. See /block-templates/README.md for detailed documentation.
Key Files
| Path | Purpose |
|---|---|
prc-block-library.php |
Plugin entry point; defines constants, runs Plugin |
includes/class-plugin.php |
Boots all blocks and support subsystems; registers block categories and HTML allowlist |
includes/class-loader.php |
Maintains and runs all WordPress action/filter hooks |
includes/utils.php |
convert_number_to_words(), prc_log_error(), prc_block_library_manifest() helpers |
src/story-item/class-ai.php |
Story Item AI title/blurb generation via the WordPress AI plugin |
includes/block-visibility/class-block-visibility.php |
Adds editor UI for per-block visibility control |
includes/custom-text-formats/class-custom-text-formats.php |
Registers custom rich text format buttons in the editor toolbar |
includes/interactivity-api/class-interactivity-api.php |
Injects interactiveNamespace and interactiveSubsumption attributes and context on every block that declares supports.interactivity |
includes/pagination/class-pagination.php |
Registers shared pagination styles/scripts consumed by listing blocks |
includes/supports/class-supports.php |
Adds sticky-state color/shadow attributes; keeps legacy maxWidth attribute registration/render (editor UI removed) |
build/blocks-manifest.php |
Auto-generated block metadata index; do not edit manually |
src/README.md |
Instructions for scaffolding a new block |
bin/build-block.js |
CLI helper for building a single named block |
bin/start-block.js |
CLI helper for watching a single named block |
webpack.config.js |
Inherits from root webpack.config.js |
tests/prc-block-library/e2e/ |
Playwright e2e specs (monorepo root) |
Blocks
This section doubles as the documentation index. Each block links to its detailed doc in docs/plugins/prc-block-library/blocks/.
Core Block Modifications (core/*)
Adds attributes, context, rendering changes, or new editor controls to existing WordPress core blocks.
| Block | Notes |
|---|---|
core/button |
PRC platform customizations |
core/categories |
PRC platform customizations |
core/carousel |
WIP, no functional implementation yet |
core/code |
PRC platform customizations |
core/cover |
PRC platform customizations |
core/details |
Interactivity API integration |
core/dialog |
PRC dialog pattern utilities |
core/embed |
PRC platform customizations |
core/file |
PRC platform customizations |
core/group |
PRC platform customizations |
core/heading |
PRC platform customizations |
core/image |
PRC platform customizations |
core/list |
PRC platform customizations |
core/list-item |
PRC platform customizations |
core/media-text |
PRC platform customizations |
core/navigation |
PRC platform customizations |
core/paragraph |
PRC platform customizations |
core/post-content |
PRC platform customizations |
core/post-title |
Custom render callback for PRC title patterns |
core/pullquote |
PRC platform customizations |
core/query-pagination-numbers |
PRC platform customizations |
core/search |
PRC platform customizations |
core/separator |
PRC platform customizations |
core/social-links |
Interactivity API integration |
core/table |
PRC platform customizations |
core/tabs |
PRC platform customizations; registered as Remote Data Blocks template |
Content & Editorial (prc-block/*)
| Block | Description |
|---|---|
prc-block/story-item |
Primary content card for linking to articles; supports block bindings and query loop context |
prc-block/popular-story |
Variant of story item for trending/popular content |
prc-block/sub-title |
Deck/subtitle display below a post title |
prc-block/post-parent-title |
Renders the parent post title with link |
prc-block/post-taxonomy-terms |
Displays taxonomy term links for the current post |
prc-block/breadcrumbs |
Breadcrumb navigation for the current post |
prc-block/table-of-contents |
Generates a TOC from heading blocks; also used by the Print Engine |
prc-block/footnotes |
Footnote rendering |
prc-block/copyright |
Copyright statement block |
prc-block/version |
Displays a version/update notice |
prc-block/promo |
Promotional content unit |
prc-block/promo-rotator |
Cycles through multiple prc-block/promo blocks |
prc-block/logo |
PRC logo block with link support |
prc-block/timeline |
Container for prc-block/timeline-slide items |
prc-block/timeline-slide |
Individual slide within a timeline |
Navigation & Layout
| Block | Description |
|---|---|
prc-block/navigation-mega-menu |
Mega-menu extension for core/navigation |
prc-block/accordion-controller |
Manages expand/collapse state across child accordions |
prc-block/accordion |
Individual accordion panel |
prc-block/carousel-controller |
Splide.js-powered carousel container |
prc-block/carousel-slide |
Individual slide within a carousel |
prc-block/tabs |
Tab container; registered as Remote Data Blocks template |
prc-block/tab |
Individual tab panel |
prc-block/collapsible |
Single show/hide content region |
prc-block/responsive-container-controller |
Renders different inner block sets at different breakpoints |
prc-block/responsive-container-view |
A breakpoint-specific view within a responsive container |
Interactive UI
| Block | Description |
|---|---|
prc-block/dialog |
Modal dialog container |
prc-block/dialog-trigger |
Button or element that opens a dialog |
prc-block/dialog-element |
Content region inside a dialog |
prc-block/flip-card-controller |
Flip card animation container |
prc-block/flip-card-side |
Front or back face of a flip card |
prc-block/show-more |
Truncates content with an expandable "show more" control |
prc-block/animation |
Applies scroll/entrance animations to inner content |
prc-block/progress-bar |
Animated progress indicator |
prc-block/render-to-region |
(In progress) Portals block content to a named page region |
prc-block/entity-as-iframe |
Embeds a WordPress post or page as a resizing iframe |
Forms
Form container and structure blocks (prc-block/form, form-page, form-submit, form-message, form-captcha, form-message-bindings) are registered by @prc/block-forms. Form input blocks remain in this plugin:
| Block | Description |
|---|---|
prc-block/form-input-text |
Text input |
prc-block/form-input-textarea |
Textarea input |
prc-block/form-input-checkbox |
Checkbox input |
prc-block/form-input-radio-group |
Radio button group |
prc-block/form-input-select |
Select/dropdown |
prc-block/form-input-select-range |
Dual-handle range select |
prc-block/form-input-range |
Single-handle range slider |
prc-block/form-input-password |
Password input |
prc-block/mailchimp-form (deprecated) |
Use Form block Newsletter Signup (Mailchimp) variation |
prc-block/mailchimp-select (deprecated) |
Use Form block Newsletter Selection (Mailchimp) variation |
Social
| Block | Description |
|---|---|
prc-block/social-share-sheet |
Social sharing action sheet |
prc-block/social-share-text-link |
Inline "share this" text link |
prc-block/social-share-url-field |
Copyable URL field for sharing |
Taxonomy & Data
| Block | Description |
|---|---|
prc-block/table |
Data table with TypeScript editor and sorting (owned by @prc/block-tables) |
prc-block/taxonomy-list |
Lists terms from a taxonomy |
prc-block/taxonomy-list-link |
Individual term link within a taxonomy list |
prc-block/taxonomy-search |
Searchable term browser |
prc-block/taxonomy-index-az-controller |
A–Z index controller for taxonomy browsing |
prc-block/taxonomy-index-az-list |
Letter-grouped term list |
prc-block/taxonomy-index-list-controller |
Paginated/filtered taxonomy list controller |
prc-block/tokens-list |
Displays a list of design tokens |
prc-block/roper-db-search |
Roper Center database search interface |
Attachments & Media
| Block | Description |
|---|---|
prc-block/attachments-list |
Lists file attachments for a post |
prc-block/attachments-pagination |
Pagination for attachment lists |
prc-block/audio-player |
HLS.js/MediaElement audio player |
Utilities & Developer Tools
| Block | Description |
|---|---|
prc-block/card |
Generic content card container |
prc-block/icon |
FontAwesome icon picker and renderer |
prc-block/color-palette |
Displays the active theme color palette |
prc-block/code-syntax |
highlight.js syntax-highlighted code block |
prc-block/lorem-ipsum |
Placeholder text generator for layout work |
prc-block/playground |
Embeds a WordPress Playground instance |
Support Systems
These subsystems are booted by Plugin and apply globally, not per-block.
Interactivity API (includes/interactivity-api/)
Injects interactiveNamespace (string) and interactiveSubsumption (boolean) attributes on every block that declares "interactivity": true in its supports. Also wires prc-block/interactiveNamespace into usesContext and providesContext so namespace values flow down the block tree automatically.
Print Engine
Print / PDF document generation lives in the standalone prc-print-engine plugin (/print endpoint and Block_Print_Registry). Block-library still registers print styles for blocks such as core/post-title on prc_print_engine_register_block_callbacks.
Block Supports (includes/supports/)
Adds sticky layout enhancements for blocks that support sticky positioning:
- Sticky enhancements —
isStuckBackground,isStuckText,isStuckBoxShadowattributes that swap colors and add a box shadow when a sticky block is in the "stuck" state via the Interactivity API. - Legacy
maxWidth— the Dimensions panel Max Width control is removed. Existing blocks that already have amaxWidthattribute keep editor and frontend styling. Prefer Gutenberg content width / layout settings for new content; custom CSS remains available for one-off cases.
The same editor script (src/index.jsx) unregisters unused core block types (archives, calendar, latest comments, tag cloud, verse) and a set of core/embed service variations; edit that file to change the list.
Form Data Store
The prc-block-library/forms @wordpress/data store is registered by @prc/block-forms (see plugins/prc-block-forms/src/form/store.js). Form types are registered through that store rather than WordPress filters.
import { dispatch } from '@wordpress/data';
dispatch('prc-block-library/forms').registerForm({
label: 'My Custom Form',
namespace: 'my-plugin/namespace',
action: 'my-action',
method: 'api',
template: [
/* block template */
],
});
Retrieve registered forms:
import { useSelect } from '@wordpress/data';
const forms = useSelect(
(select) => select('prc-block-library/forms').getForms(),
[]
);
Hooks & Filters
| Hook | Type | Description |
|---|---|---|
block_categories_all |
filter | Adds five categories: Content Curation, Marketing, Forms, Editorial Product, Interactivity API |
should_load_separate_core_block_assets |
filter | Returns true; enables per-block asset loading for performance |
wp_kses_allowed_html |
filter | Extends allowed tags to include iframe, input, textarea, form, picture, source, svg, path, rect; extends img with srcset/sizes; extends a with aria attributes |
safe_style_css |
filter | Adds container and @container to allowed inline CSS properties |
block_type_metadata |
filter | Multiple usages — injects interactiveNamespace/interactiveSubsumption, printEngine, sticky attributes, and legacy maxWidth on all blocks |
block_type_metadata_settings |
filter | Merges Interactivity API context entries for blocks that support it |
render_block |
filter | Applied by Print Engine (visibility and data-* attribute injection) and Supports (sticky and legacy max-width rendering) |
remote_data_blocks_template_blocks |
filter | Signals to Remote Data Blocks that prc-block/tabs and core/tabs support RDB templates |
remote_data_blocks_register_example_block |
filter | Returns false; disables the RDB example block |
query_vars |
filter | Adds print, printEngineBeta, and pdf to recognized query vars |
prc_print_engine_register_block_callbacks |
action | Fires at init priority 5; use this to register print callbacks via Block_Print_Registry |
prc_print_engine_block_{block_name} |
filter | Per-block filter on print-rendered HTML; fires after the registered Block_Print_Registry callback |
wpai_register_features |
action | Used internally to register block library AI features (tabular data, story blurb) with the WordPress AI plugin |
Block patterns (disposition-A scaffolds)
Patterns live in patterns/ and register via register_plugin_patterns() from client-mu-plugins/plugin-patterns.php. Category slug: prc-block-library.
| Retired variation | Pattern slug | Block Types header |
|---|---|---|
| Callout | prc-block-library/callout |
core/group |
| Social Group | prc-block-library/social-group |
core/group |
| Collapsible | prc-block-library/collapsible |
core/details |
| Pew Knight co-branded | prc-block-library/pew-knight-co-branded |
core/details |
Use the Patterns inserter (prc-block-library category) for these scaffolds.
core/group width presets (transform-scoped variations)
Constrained-width group presets (200px, 250px, 300px, 320px, 420px, 640px) register as core/group block variations with scope: ['transform'] in src/core-group/variations.js. They appear in the block transforms menu when converting or transforming a Group block, not in the block inserter, Patterns inserter, or variation switcher.
core/heading presets (transform-scoped variations)
Heading (H4 default), Section (isChapter: true, level 3), and Layout Heading (is-style-layout-heading) register as core/heading block variations with scope: ['transform'] in src/core-heading/index.js. They appear in the block transforms menu only.
Binding companions (patterns + inserter variations)
| Discovery | Item | Where |
|---|---|---|
| Pattern | Copyright Disclaimer | prc-block-library/copyright-disclaimer — Patterns inserter |
Inserter variation (scope: ['inserter']) |
Tab Label | core/paragraph + core/heading inside core/tab-panel |
| Inserter variation | Dialog Label | core/heading inside prc-block/dialog-element |
| Inserter variation | Form: Result Message | core/paragraph inside prc-block/form-message |
| Inserter variation | Version Info | core/paragraph (global inserter) |
Copyright disclaimer remains a parent-scoped pattern. Tab label, dialog label, form result message, and version info register as block variations visible in the block inserter only (not the variation switcher). Use Attributes → Bindings to retrofit existing blocks.
Author discovery (bindings, patterns, bits)
| Author task | Preferred path |
|---|---|
| Layout scaffold (group width, callout, collapsible, heading presets) | Block pattern under prc-block-library or prc-religious-landscape-study |
| Staff field on a whole block (photo, bio, job title) | Staff Context Provider → pattern or Attributes → Bindings → Staff Info API |
| Staff name/title/bio inline in copy | Staff Context Provider → block bits toolbar (Staff category) |
| Quiz / typology field in results | Parent block → pattern or Attributes → Bindings (ancestor-scoped source) |
| User login/profile/logout button URL | User State panel → Attributes → Bindings → User Accounts |
| Copyright year in running text | Block bits → Copyright |
Troubleshooting
Block class not found on activation
Symptom: Fatal error referencing a missing class (e.g. Core_Button) on plugin load.
Cause: A block directory exists in build/ but its class-{block-name}.php file is missing — usually because the library was not built after a fresh clone.
Fix: Run npm run build:library -w @prc/block-library from the repo root.
Block not appearing in the editor
Symptom: A block is not visible in the block inserter after adding its directory to src/.
Cause: The new block class was not initialized in Plugin::define_prc_blocks() or Plugin::define_core_blocks(), or its directory name starts with _ or ..
Fix: Add new My_Block($this->get_loader()); in the appropriate method in includes/class-plugin.php and rebuild.
?pdf=true page is blank or crashes
Symptom: The print/PDF view renders nothing or throws a PHP error.
Cause: The Print Engine exits early if is_singular() is false, or if $post is null. Can also happen when the prc-block/table-of-contents block's render.php is not built.
Fix: Confirm the URL is for a singular post type, that the library is fully built, and that prc-block/table-of-contents appears in build/blocks-manifest.php.