Custom controls and a counter
Deck.useDeck() (see Hooks) gives your own components what the
built-in controls read. Here Previous, Next, a button per page and a counter
are built from a design system's button, whose API is not a native button's:
One
Two
Three
Four
import * as Deck from '@carouseldeck/react';
// Your design system's button.
import { Button } from './design-system';
/** Previous, Next, a button per page and a counter, from your own button. */
function Controls() {
const { index, count, fits, canPrev, canNext, prev, next, scrollTo } =
Deck.useDeck();
// Every slide fits: there is nowhere to go.
if (fits) return null;
return (
<div className="controls">
<Button isDisabled={!canPrev} onPress={prev}>
Previous
</Button>
{/* Pages, as Deck.Dots: none until the deck is measured. */}
<div role="group" aria-label="Choose page">
{Array.from({ length: count ?? 0 }, (_, page) => (
<Button
key={page}
aria-label={`Go to page ${page + 1}`}
aria-current={page === index ? 'true' : undefined}
onPress={() => scrollTo(page)}
>
{page + 1}
</Button>
))}
</div>
<span>{count !== null && `${index + 1} / ${count}`}</span>
<Button isDisabled={!canNext} onPress={next}>
Next
</Button>
</div>
);
}
export function Products() {
return (
<Deck.Root aria-label="Featured slides">
<Deck.Viewport>
<Deck.Slide>One</Deck.Slide>
<Deck.Slide>Two</Deck.Slide>
<Deck.Slide>Three</Deck.Slide>
<Deck.Slide>Four</Deck.Slide>
</Deck.Viewport>
<Controls />
</Deck.Root>
);
}They do what the built-in controls do:
- They render nothing when every slide fits.
- Previous and Next are disabled at the ends, unless the deck loops.
- The page buttons are a group labelled "Choose page", the current page's
marked
aria-current, asDeck.Dots. There are none until the deck is measured, sincecountisnullon the server. Deck.Root's live region still announces the slide the deck moves to, so the counter needs no live region of its own.- A move stops autoplay, as a move with a built-in control does.
The story is Recipes / Custom Controls, where ./design-system is a
stand-in.