Skip to main content

Components

Role

src/themes/reactwp/js/components/ contains the reusable React building blocks shipped with the starter.

These components are intentionally small. They are not a design system by themselves. Their job is to cover the core patterns ReactWP needs often:

  • internal links with route prefetching
  • button rendering
  • rich text output
  • editorial content blocks
  • media placeholders for the loader runtime
  • shell-level header and footer mounting

Available Components

  • AppLink.jsx
  • Audio.jsx
  • Button.jsx
  • Contents.jsx
  • Footer.jsx
  • Header.jsx
  • Image.jsx
  • RichText.jsx
  • Video.jsx
  • Wrapper.jsx

Component Categories

  • AppLink
  • Button

Content Rendering

  • Contents
  • RichText
  • Wrapper

Media Placeholders

  • Image
  • Video
  • Audio

Shell Components

  • Header
  • Footer

AppLink is the base component for internal navigation.

It wraps React Router's Link, but also prefetches the next route on hover and focus through the loader runtime.

Use it when:

  • you are linking to an internal route
  • you want React navigation and transitions
  • you want route prefetching

Useful props:

  • to
  • data-router
  • updateHash
  • onMouseEnter
  • onFocus
  • any normal link props such as className, target, or rel

to can be:

  • a string path
  • a React Router location-like object with pathname, search, and hash

If data-router="false" or data-router={false} is passed, AppLink falls back to a normal <a> instead of React Router. In that mode it does not use the route prefetch behavior.

Same-page anchor links scroll through ReactWP's scroller. By default, updateHash is true, so a normal click also adds the target hash to the browser URL and history. Set updateHash={false} when the scroll is an interface convenience that should not change the visible URL or create a history entry. The rendered link keeps its real href, so copying the link and using a modified click still preserve the anchor destination.

Example:

import AppLink from '../components/AppLink';

const Example = () => {
return <AppLink to="/">Home</AppLink>;
};

Example without router interception:

<AppLink to="/wp-admin/" data-router={false}>
Open WordPress Admin
</AppLink>

Same-page scroll without changing the URL:

<AppLink to="#pricing" updateHash={false}>
View pricing
</AppLink>

Button

Button is the main action component.

It chooses the correct underlying element automatically:

  • no to and no href -> renders a <button>
  • external href or to such as https://, mailto:, or tel: -> renders a normal <a>
  • internal to or href -> renders AppLink

Useful props:

  • to
  • href
  • text
  • children
  • before
  • after
  • variant
  • updateHash
  • className
  • any normal button or link props such as type, target, rel, or data-router

Important defaults and behavior:

  • variant defaults to primary
  • text is used when present, otherwise children becomes the main label
  • before and after render inside .button__before and .button__after
  • when Button renders an internal AppLink, it forwards updateHash
  • string values in before, after, text, or children are rendered as escaped JSX text, not interpreted as HTML
  • forwarded DOM props are filtered by sanitizeDomProps

Examples:

<Button onClick={handleClick}>Open modal</Button>

React supplies the click event to onClick. Use event.currentTarget when GSAP needs the actual button element:

<Button
text="Open panel"
onClick={(event) => {
gsap.to(event.currentTarget, { scale: 0.98, duration: 0.2 });
}}
/>

event.target can be a nested label or decoration inside the button. currentTarget is the element that owns the handler.

React removes its component event handlers when the component unmounts. Clean up timelines, ScrollTriggers, timers, observers, or manually attached native listeners that the handler creates; the JSX onClick itself does not need a manual kill.

<Button to="/contact/" variant="primary">
Contact us
</Button>
<Button
href="https://example.com"
before="Read"
after="now"
>
External article
</Button>
<Button href="/wp-admin/" data-router={false}>
Open WordPress Admin
</Button>
<Button href="#pricing" updateHash={false}>
View pricing
</Button>

Contents

Contents is a convenience content block for common editorial layouts.

It can render:

  • an uptitle
  • a title
  • a subtitle
  • text
  • a list of buttons

Useful props:

  • uptitle
  • title
  • subtitle
  • text
  • buttons
  • titleTag
  • className
  • other safe DOM props you want forwarded to the outer .contents node

Important defaults and behavior:

  • titleTag defaults to h2
  • if every content prop is empty, Contents returns null
  • text is rendered through Wrapper, not directly through RichText
  • string text is escaped JSX text; pass a React node when structured markup is needed
  • buttons is mapped through Button
  • each button object can use either to or url
  • new_tab is converted to target="_blank" when target is not already provided

titleTag is useful when the same content pattern needs different document semantics.

Examples:

<Contents
uptitle="ReactWP"
title="Project overview"
subtitle="Start from a clean baseline"
titleTag="h1"
text="Start by customizing your templates, site settings, and frontend runtime."
/>
<Contents
title="Section title"
titleTag="h3"
text="This can also be a subsection inside a longer page."
/>
<Contents
uptitle="ReactWP"
title="Project overview"
subtitle="Start from a clean baseline"
text="Start by customizing your templates, site settings, and frontend runtime."
buttons={[
{
text: 'Open home',
to: '/'
},
{
text: 'Open admin',
href: '/wp-admin/',
data-router: false,
new_tab: true
}
]}
/>

Button objects are forwarded to Button.jsx. In practice, the most useful keys are:

  • text
  • to
  • url
  • href
  • target
  • new_tab
  • before
  • after
  • variant
  • updateHash
  • data-router

RichText

RichText is ReactWP's transformed path for supported WordPress HTML. It parses a string with html-react-parser, removes unsupported nodes, filters attributes, validates URL-bearing values and srcset, and normalizes links that open a new tab.

Use it when React must transform the HTML tree during rendering, for example to remove nodes, change attributes, or rebuild supported markup. The parser is not a general-purpose sanitizer, so WordPress/ACF/API HTML must still be sanitized according to the backend content contract.

If value is already a React node, it is rendered directly inside a <div>.

Important behavior:

  • falsy value returns null
  • className defaults to an empty string
  • the shipped component accepts only value and className; it does not forward arbitrary props
  • string input is bounded and limited to the tags and attributes supported by the component

Example:

<RichText
className="copy"
value="<p>This content comes from WordPress or ACF.</p>"
/>

For backend-sanitized HTML that must remain unchanged and needs no React-level transformation, prefer a small explicit rendering boundary:

const SanitizedHtml = ({ html = '', className = '' }) => (
<div
className={className}
dangerouslySetInnerHTML={{ __html: html }}
/>
);

Only pass HTML that the backend contract has already sanitized to this boundary.

Wrapper

Wrapper renders a custom <rwp-wrap> element around value.

Useful props:

  • value
  • other safe DOM props you want forwarded to <rwp-wrap>

Example:

<Wrapper value={text} />

This is mainly useful when the project styles or scripts rely on that wrapper element.

String values are rendered as escaped JSX text. If you do not need the custom wrapper behavior, use ordinary JSX for plain text, an explicit unchanged-HTML boundary for already sanitized HTML, or RichText when React-level HTML transformations are required.

Image, Video, and Audio

These components are placeholder containers, not classic media tags with src props.

They render the shell expected by the loader runtime:

  • Image -> .img-container > .inner-img > .img
  • Video -> .video-container > .inner-video > .video
  • Audio -> .audio-container > .inner-audio > .audio

Useful props:

  • className
  • filtered DOM props such as id, data-*, or aria-*
  • forwarded ref

Example:

<Image className="hero-media" />

These components make the most sense when your project uses ReactWP's critical or non-critical media pipelines.

They are useful because the loader can swap the placeholder with the real media node later, after preloading.

Header is a shell-level component rendered from React, but mounted outside the smoother through a portal.

Useful props:

  • show
  • className
  • mountId
  • other safe DOM props you want forwarded to the final <header> node

Important behavior:

  • if show is false, it renders nothing
  • if show is true, it portals into the element identified by mountId
  • default mountId is app-header
  • the mount node should live outside #pageWrapper and #pageContent
  • if the mount node is missing, it falls back to document.body

Important limitation of the shipped component:

  • the default Header.jsx is only an empty shell
  • it does not render navigation, site data, or branding on its own
  • extra props pass through sanitizeDomProps before they reach the final <header> element

If you want to pass navigation, site, or other runtime data, edit Header.jsx to consume those props explicitly. Do not rely on the DOM-prop filter as an application-data transport.

Example shell usage:

<AppShell
showHeader={true}
headerProps={{
className: 'site-header',
mountId: 'app-header'
}}
>
{children}
</AppShell>

If your header needs menu data, a more realistic pattern is:

import { sanitizeDomProps } from '../inc/domProps';

const Header = ({
show,
className,
mountId = 'app-header',
navigation = {},
...domProps
}) => {
if(!show){
return null;
}

const safeDomProps = sanitizeDomProps(domProps);

return createPortal(
<header className={className} {...safeDomProps}>
<Navigation navigation={navigation} />
</header>,
document.getElementById(mountId)
);
};

If you use a custom mountId, the matching node must exist in the PHP markup:

<div id="my-header-mount"></div>

For the shell structure that makes this work, see Theme Shell and Scroll.

Footer is simpler than Header.

Useful props:

  • show
  • className
  • other safe DOM props you want forwarded to the <footer> node

Important behavior:

  • if show is false, it renders nothing
  • if show is true, it renders an empty <footer> shell
  • like Header, the shipped version does not render any actual content by itself
  • forwarded DOM props pass through sanitizeDomProps

Example:

<AppShell
showFooter={true}
footerProps={{
className: 'site-footer'
}}
>
{children}
</AppShell>

In practice, most projects will customize Footer.jsx quickly.