This block uses the WordPress Interactivity API to manage state and logic. You can consume this state in your own custom blocks to build advanced features like progress bars, slide counters, or synchronized sliders.
carousel-kit/carousel
The following properties are exposed in the Interactivity API context:
| Property | Type | Description |
|---|---|---|
isPlaying |
boolean |
true if Autoplay is currently running. |
timerIterationId |
number |
Increments every time the Autoplay timer resets (slide change). Bind to key to restart animations. |
autoplay |
boolean or { delay, ... } |
Autoplay config or false if disabled. |
canScrollPrev |
boolean |
true if there are previous slides (or looping). |
canScrollNext |
boolean |
true if there are next slides (or looping). |
selectedIndex |
number |
The zero-based index of the current slide. |
scrollSnaps |
{ index: number }[] |
List of snap points (used by Dots block). |
Create a progress bar that animates while the carousel is auto-playing.
save.tsx
<div
data-wp-interactive="carousel-kit/carousel"
data-wp-bind--key="context.timerIterationId"
data-wp-bind--style="state.barDuration"
data-wp-class--is-playing="context.isPlaying"
className="core-progress-bar"
/>view.ts
store('carousel-kit/carousel', {
state: {
get barDuration() {
const { autoplay } = getContext();
return `--duration: ${autoplay?.delay || 4000}ms;`;
}
}
});CSS
.core-progress-bar.is-playing {
animation: grow var(--duration) linear forwards;
}
@keyframes grow { from { width: 0%; } to { width: 100%; } }Highlight slide content when active using the callbacks.isSlideActive helper.
<div
data-wp-interactive="carousel-kit/carousel"
data-wp-class--is-active="callbacks.isSlideActive"
className="my-card"
>
<h2 data-wp-class--highlight="callbacks.isSlideActive">Card Title</h2>
</div>Gutenberg's InnerBlocks can isolate React Contexts, causing state sync issues between parent and deeply nested children in the editor. To guarantee reliable interactivity:
- Attach: The Viewport component attaches the vanilla Embla instance directly to its DOM node using a Symbol key:
element[Symbol.for("carousel-kit.carousel")] = embla. - Find: Child components (Controls/Dots) attempt to find the API via Context first. If missing, they traverse the DOM up to the common wrapper (
.carousel-kit) and then search for the sibling.emblaviewport. - Bind: A retry mechanism (
setTimeout+useEffect) ensures the Viewport has finished initializing before binding listeners.
The Carousel Dots block demonstrates a pattern for iterating over data with the Interactivity API:
- Data Source:
context.scrollSnapsis populated inview.tsby mapping Embla's snap list to objects:[{ index: 0 }, { index: 1 }, ...]. - The Loop:
The
<template data-wp-each--snap="context.scrollSnaps"> <button data-wp-class--is-active="callbacks.isDotActive" ... /> </template>
--snapsuffix names the iterator variable. - Why Objects? The Interactivity API's loop directive does not expose a separate
indexvariable. To check if a dot is active (selectedIndex === currentDotIndex), we include the index explicitly inside the data object (context.snap.index).