@taktdev/components
v1.1.0-beta.22
Published
WordPress Gutenberg components to be used on themes
Downloads
1,065
Readme
Takt WordPress Components
A collection of reusable React components for building custom Gutenberg authoring experiences as part of Takt's WordPress ensemble. These components are designed to streamline editor development and provide advanced UI features for block and panel creation.
Components Overview
Below is a brief description of each component included in this package:
AdvancedRichText
An enhanced RichText component for Gutenberg blocks, supporting custom formatting, tag filtering, and optional line break disabling.
Props:
value(required): stringonChange(required): functionallowedFormats(optional): string[]disableLineBreaks(optional): boolean- ...all
RichTextProps from@wordpress/block-editor
Appender
A wrapper for the default Gutenberg block appender, allowing for custom styling and positioning when adding new blocks.
Props:
className(optional): stringclientId(optional): stringstyle(optional): object
BaseButton
Thin wrapper over BaseLink with button-style defaults. Exposes a variation prop (default 'primary') and, by default, shows a variation toggle in the popover that the theme can override or disable.
BaseButton is for composition: themes wrap it with their own component (e.g. ThemeButton) that injects styling classes based on the variation. The component itself ships zero styling.
Props: all BaseLink props except children, allowedFormats, and variations, plus:
variation(optional):'primary' | 'secondary' | 'tertiary' | string— variation key (default'primary')variations(optional):BaseLinkVariations—{ options: { value: string; label: string }[]; value: string; label?: string }. Overrides the default primary/secondary/tertiary setallowVariationChange(optional): boolean — show the variation toggle in the popover (defaulttrue)
Exported types: BaseButtonProps, BaseButtonVariationTypes, BaseButtonVariations
BaseHeading
A structural section-heading assembly: eyebrow + heading + description + buttons. Ships zero styling — each slot accepts a *ClassName prop so a theme wrapper can inject its own classes. Mirrors the PHP BaseHeading renderer for editor/front-end parity.
Props:
Content
eyebrow(optional): stringheading(optional): stringdescription(optional): stringbuttons(optional):BaseHeadingButton[]—LinkData & { variation?: string }
Structure
headingTag(optional):'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'(default'h2')headingId(optional): string — id on the heading element (for in-page anchors)
Toggles
enableEyebrow/enableHeading/enableDescription/enableButtons(optional): boolean (defaulttrue)
Description mode
longFormDescription(optional): boolean (defaultfalse) — whentrue, renders the description withLongFormRichText(paragraphs, inline headings, lists). Whenfalse, renders withAdvancedRichText(single paragraph with inline formatting).
ClassName slots
className,topGroupClassName,eyebrowClassName,headingClassName,bottomGroupClassName,descriptionClassName,buttonsClassNamebuttonClassName(optional):(button: BaseHeadingButton, index: number) => string— per-button class resolver
Button config
buttonVariations(optional):string[]— ordered variations (default['primary', 'secondary', 'tertiary'])resolveButtonDefaults(optional):(button: BaseHeadingButton, index: number) => Partial<BaseHeadingButton>— compute index-dependent defaults for a button. Returned keys fall back into the button when it lacks an explicit value, and are frozen onto the button data at reorder time so anything defaulted-by-index (variation, accent, icon side, …) follows the button to its new position instead of staying pinned to the old one. The built-invariationdefault frombuttonVariationsis always applied; values returned here override it and add any other per-index defaults the theme needs. Use this instead of resolving index-dependent fields at render time inside yourbuttonComponent— otherwise a reorder will leave those fields behind.buttonComponent(optional):BaseHeadingButtonComponent— substitute component for the per-button render call. Defaults toBaseButton. Lets themes render every button inside a heading through their ownThemeButtonwrapper without reimplementingBaseHeading. The component must forward itsonChangeprop, otherwise buttons will appear read-only when rendered insideBaseHeading.<BaseHeading buttons={[ /* ... */ ]} buttonComponent={ThemeButton} />
Placeholders
eyebrowPlaceholder,headingPlaceholder,descriptionPlaceholder,buttonPlaceholder(optional): string
Plus onChange and all <div> HTML attributes except onChange.
Exported types: BaseHeadingProps, BaseHeadingButton, BaseHeadingButtonComponent
BaseLink
Foundational link component designed for theme composition. Renders an <a> with post/taxonomy URL resolution, new-tab handling, and a popover editor with extensible slots. Unlike the older Link component, BaseLink supports custom popover content via popoverBefore / popoverAfter, a configurable variations picker, and any standard anchor HTML attribute via prop spread.
Use BaseLink (and the BaseButton / BaseHeading built on it) when building theme-level wrappers. Keep Link / Button for their existing call sites.
Props:
link(optional):{ title, url, postId?, postType?, opensInNewTab?, label? }onChange(optional):(payload) => voidchildren(optional): ReactNodebefore(optional): ReactNodeafter(optional): ReactNodetagName(optional): string (default'span') — tag for the title elementallowedFormats(optional): string[] — inline formats allowed in the title (default[])placeholder(optional): stringclassName(optional): string — class on the<a>titleClassName(optional): string — class on the title elementanchor(optional): HTMLElement — popover anchor overrideopenPopupOnClick(optional): boolean (defaulttrue)popupVisible(optional): boolean — controlled popover visibilityshowTitleInPopover(optional): boolean — expose a title field inside the popoverfocusOnMount(optional): string | booleanvalidateLink(optional): boolean — verify post/term exists and is publishedautoUpdateLink(optional): boolean — refresh the stored URL from the post if it changedpopoverBefore(optional): ReactNode — custom content above the LinkControlpopoverAfter(optional): ReactNode — custom content below the LinkControlvariations(optional):BaseLinkVariations—{ options: { value: string; label: string }[]; value: string; label?: string }. Shows a variation toggle in the popover.
Plus all <a> HTML attributes (via AnchorHTMLAttributes) except onChange.
DOM hooks: the rendered <a> carries data-empty="" when the link has no url and no postId. Themes can use [data-empty] to style unconfigured buttons — e.g. dim a trailing arrow glyph to signal that the link is still empty. The attribute is omitted once either url or postId is set. Because BaseButton wraps BaseLink, the same hook is available there.
Exported types: BaseLinkProps, BaseLinkVariations, BaseLinkChangePayload
LinkEditor
The controlled, container-agnostic body of the link-editing popover used internally by Link, BaseLink, and Button: the LinkControl URL search, "open in new tab" toggle, aria-label field, and an optional variation picker. It renders no Popover and no anchor, so you can drop it into your own Popover, an InspectorControls panel, or any custom container — reusing the link-editing UI without the anchor/popover machinery.
Props:
value(required):LinkValue—{ title, url, postId?, postType?, opensInNewTab?, label? }onChange(required):(value: LinkChangePayload) => voidshowTitle(optional): boolean (defaultfalse) — showLinkControl's title field; also drives the title-sync rule applied to incoming changessyncTitle(optional): boolean — force-enable/disable adopting the control's title regardless ofshowTitle(e.g.Buttonpassesfalseso the inline title is never overwritten by the popover)variations(optional):LinkEditorVariations—{ options: { value: string; label: string }[]; value: string; label?: string }before/after(optional): ReactNode — slots rendered before/after the standard field stack
Exported types: LinkEditorProps, LinkEditorVariations
Usage (InspectorControls):
import { LinkEditor } from "@taktdev/components";
import { InspectorControls } from "@wordpress/block-editor";
import { PanelBody } from "@wordpress/components";
<InspectorControls>
<PanelBody title="Link">
<LinkEditor
value={link}
onChange={(next) => setAttributes({ link: { ...link, ...next } })}
showTitle
/>
</PanelBody>
</InspectorControls>Button
A flexible button component with support for different variations (primary, secondary, tertiary), link integration, and advanced controls for editing button properties in the editor.
Props:
link(optional): { title, url, postId?, postType?, opensInNewTab?, label? }className(optional): stringonChange(optional): functionplaceholder(optional): stringfocusOnMount(optional): string | booleanvariation(optional): 'primary' | 'secondary' | 'tertiary'allowVariationChange(optional): booleanbefore(optional): ReactNodeafter(optional): ReactNodevalidateLink(optional): booleanautoUpdateLink(optional): booleanmergeRef(optional): boolean- ...all anchor element attributes except
onChange
FilteredServerSideRender
A server-side render component with additional filtering and query options, useful for rendering dynamic block previews with custom query selectors and attributes.
Props:
block(optional): stringattributes(optional): objecturlQueryArgs(optional): objecthttpMethod(optional): 'GET' | 'POST'url(optional): stringEmptyResponsePlaceholder(optional): React.ComponentType | nullLoadingResponsePlaceholder(optional): React.ComponentType | nullquerySelector(optional): stringclientId(optional): stringclassName(optional): stringserverSideRenderRef(optional): anypreventInteraction(optional): boolean
ImageDropUploader
A drag-and-drop image uploader for Gutenberg, supporting image selection, upload, and focal point editing.
Props:
image(optional): ImageProps | numberonSelect(optional): functionallowedTypes(optional): string[]className(optional): stringimageClassName(optional): stringimageStyle(optional): objectplaceholderClassName(optional): string
IconPicker
A toolbar-based icon picker for Gutenberg blocks, supporting both suggested (predefined) icons and custom icons from the WordPress media library. Renders the selected icon inline where the component is placed.
Props:
value(optional):{ slug?: string; mediaId?: number }onChange(required):(value: IconPickerValue | null) => voidsuggestedIcons(optional):SuggestedIcon[]— array of{ name: string; label: string; icon: React.FC<React.SVGProps<SVGSVGElement>> }iconSize(optional): number (default: 48) — size of icons in the popover grid- ...all
<span>attributes exceptonChange(applied to the icon wrapper)
Exported Types:
SuggestedIcon—{ name: string; label: string; icon: React.FC<React.SVGProps<SVGSVGElement>> }IconPickerValue—{ slug?: string; mediaId?: number }IconPickerProps
Usage:
import { IconPicker, SuggestedIcon } from "@taktdev/components";
import { ReactComponent as IconYoutube } from "./resources/icon-youtube.svg";
const suggestedIcons: SuggestedIcon[] = [
{ name: "youtube", label: "YouTube", icon: IconYoutube },
];
<IconPicker
value={iconSlug ? { slug: iconSlug } : mediaId ? { mediaId } : null}
onChange={(icon) => setAttributes({
iconSlug: icon?.slug ?? undefined,
logoId: icon?.mediaId ?? undefined,
})}
suggestedIcons={suggestedIcons}
iconSize={24}
/>ImageOrInlineSvg
Displays an image by ID or attachment, with special handling for inline SVGs (fetches and injects SVG markup for SVG images).
Props:
imageId(optional): numberattachment(optional): Attachment- ...all standard
<img>attributes
Link
A link component with advanced controls for editing link properties, including support for post links, new tab toggling, and custom labels.
Props:
children(optional): ReactNodebefore(optional): ReactNodeafter(optional): ReactNodelink(optional): { title, url, postId?, postType?, opensInNewTab?, label? }className(optional): stringtitleClassName(optional): stringonChange(optional): functiontagName(optional): stringallowedFormats(optional): []placeholder(optional): stringanchor(optional): HTMLElementfocusOnMount(optional): string | booleanvalidateLink(optional): booleanautoUpdateLink(optional): booleanopenPopupOnClick(optional): booleanpopupVisible(optional): booleanshowPopupTitleField(optional): boolean
MediaUploadPanel
A media upload panel with optional focal point picker, designed for selecting and editing images or other media within block controls.
Props:
media(optional): ImageProps | numberlabel(optional): stringonSelect(optional): functionallowedTypes(optional): string[]enableFocalPoint(optional): boolean
LongFormRichText
A multi-block rich-text editor that lets authors mix paragraphs, inline headings (h1–h6), and ordered/unordered lists inside a single HTML attribute. A toolbar dropdown switches block types, Enter creates new blocks, and Backspace merges them — similar to the block editor's own paragraph flow but scoped to one attribute.
Useful for description or body-copy fields where a single paragraph isn't enough, but a full InnerBlocks canvas would be too much ceremony.
Props:
value(required): string — serialized HTMLonChange(required):(html: string) => voidallowedBlocks(optional):LongFormBlockType[]— which block types the author can switch to. Default:['paragraph', 'h3', 'h4', 'ul', 'ol']allowedFormats(optional): string[] — inline formats allowed inside each block. Default:['core/bold', 'core/italic', 'core/link']className(optional): stringplaceholder(optional): string
Exported types:
LongFormBlockType—'paragraph' | 'h1' | ... | 'h6' | 'ul' | 'ol'
PopoverExternalLink
A popover input for external links, phone numbers, or emails, with protocol handling and inline editing UI.
Props:
children(optional): ReactNodevalue(optional): stringonChange(optional): functionlabel(optional): stringplaceholder(optional): stringclassName(optional): stringtype(optional): 'link' | 'phone' | 'email'includeProtocol(optional): boolean
PostSelectorSortable
A sortable post selector component for choosing and reordering posts of any type, with support for thumbnails and custom limits.
Props:
value(required): number[] | numberonChange(required): functionpostTypes(optional): string[]limit(optional): number | stringtitle(optional): stringonDocumentPanel(optional): booleanforceReturnAsArray(optional): booleandisplayThumbnails(optional): boolean
TaxonomySelect
A taxonomy selector with multi-select and sorting, allowing users to pick and order taxonomy terms (categories, tags, etc.) in the editor.
Props:
title(optional): stringvalue(required): number[]taxonomy(optional): stringonChange(optional): functionselectedFirst(optional): booleandisplaySearch(optional): booleanlimitHeight(optional): booleanHierarchical(optional): boolean
Hooks & helpers
Reusable pieces of the link-editing logic, extracted from Link / BaseLink / Button so custom link UIs (custom popovers, inspector panels) don't have to reimplement them.
useLinkField
Owns the WordPress-data side of a link field: resolves a postId / postType reference (post or taxonomy term) to its canonical URL and, optionally, validates and auto-corrects the stored URL against it.
Signature: useLinkField(link: LinkValue, options?) => { isValidLink: boolean }
Options:
validateLink(optional): boolean — reportisValidLink: falsewhen the referenced entity no longer resolvesautoUpdateLink(optional): boolean — emitonChangewith the refreshed URL when the resolved permalink drifts from the stored oneonChange(optional):(value: LinkChangePayload) => void
mapLinkControlValue
Pure helper that maps a LinkControl onChange value back onto the LinkValue model — extracts the numeric postId / postType (only numeric ids are entity references) and applies the title-sync rule.
Signature: mapLinkControlValue(link: LinkValue, value, { showTitle?, syncTitle? }) => LinkChangePayload
useLinkPopover
Owns the popover open/close lifecycle shared by the anchor-rendering link components: visibility, popupVisible sync, inline-vs-toolbar trigger source, the anchor/popover refs, and an iframe-aware close (correct when focus moves across the editor-canvas iframe boundary).
Options: { popupVisible?: boolean; openPopupOnClick?: boolean }
Returns: { isVisible, setIsVisible, triggerSource, buttonRef, setPopoverNode, toolbarAnchor, setToolbarAnchor, closePopover, open, handleFocus, handleBlur, toggleFromToolbar } — wire buttonRef to the <a>, setPopoverNode to the Popover ref, and setToolbarAnchor to a toolbar button when offering a toolbar trigger.
Shared types:
LinkValue—{ title: string; url: string; postId?: number | null; postType?: string | null; opensInNewTab?: boolean; label?: string }LinkChangePayload—Partial<LinkValue> & { variation?: string }
For detailed usage and props, see the source code for each component in the src/ directory.
