Guia para React

Aprenda a montar sliders com @reelkit/react.

Toque em primeiro lugar
Deslize com inércia e encaixe
Navegação por teclado
Setas e Escape
Roda do mouse
Opcional, com debounce
Virtualizado
Mais de 10.000 itens, 3 no DOM
Indicadores
Pontinhos deslizantes no estilo Instagram
API programática
next(), prev(), goTo() pelo ref
Modo cíclico
Navegação circular sem fim
Nos dois sentidos
Vertical ou horizontal
Zero re-renderizações
Estado atualizado por sinais

Componente Reel

O componente Reel é o contêiner principal: cuida do estado do slider, dos gestos de toque, da navegação por teclado e das animações.

Só três slides ficam no DOM, o atual e os vizinhos, então uma lista de dez mil itens custa o mesmo que uma de dez. Para ver isso funcionando em um recurso completo, como um feed infinito ou uma grade que abre um player, veja as Receitas.

tsx
import { Reel, ReelIndicator } from '@reelkit/react';

<Reel
  count={items.length}
  size={[width, height]}
  direction="vertical"
  enableWheel
  afterChange={(index) => console.log('Current:', index)}
  itemBuilder={(index, indexInRange, size) => (
    <div style={{ width: size[0], height: size[1] }}>
      Slide {index}
    </div>
  )}
>
  {/* Optional children like ReelIndicator */}
</Reel>

Dimensionamento automático

A prop size é opcional. Sem ela, o Reel mede o próprio contêiner com ResizeObserver e acompanha o layout definido no CSS. O contêiner precisa ganhar tamanho do elemento pai (flex, grid ou dimensões explícitas no CSS).

tsx
// Explicit size (fixed)
<Reel count={items.length} size={[400, 600]} itemBuilder={...} />

// Auto-size (responsive — sized by CSS)
<Reel count={items.length} style={{ width: '100%', height: '100dvh' }} itemBuilder={...} />

Padrão itemBuilder

A prop itemBuilder é uma função que recebe o índice e devolve o conteúdo de cada slide. Esse padrão é o que viabiliza a virtualização — apenas os itens visíveis são renderizados.

tsx
itemBuilder={(index, indexInRange, size) => {
  // index: actual item index (0 to count-1)
  // indexInRange: position in visible window (0, 1, or 2)
  // size: [width, height] of the container
  return <Slide index={index} />;
}}

Formas de navegação já incluídas:

  • Toque/Deslizar: arraste para navegar, com inércia e encaixe
  • Teclado: setas e Escape
  • Roda do mouse: ative pela prop enableWheel
  • Programática: use apiRef para next(), prev(), goTo()
tsx
import { useRef } from 'react';
import { Reel, type ReelApi } from '@reelkit/react';

function App() {
  const apiRef = useRef<ReelApi>(null);

  return (
    <>
      <Reel
        count={10}
        size={[400, 600]}
        apiRef={apiRef}
        itemBuilder={(index) => <Slide index={index} />}
      />
      <button onClick={() => apiRef.current?.prev()}>Prev</button>
      <button onClick={() => apiRef.current?.next()}>Next</button>
      <button onClick={() => apiRef.current?.goTo(5)}>Go to 5</button>
    </>
  );
}

Estado na URL

useOverlayUrlState monta um controlador de estado na URL para o overlay e devolve o controlador inteiro; você o entrega a um *UrlOverlay pela prop controller. Quem manda no estado aberto é a URL, então um overlay ligado a ela se abre sozinho e o link vira a forma normal de abrir. A primeira escrita de um parâmetro ausente empilha uma entrada no histórico e cada escrita seguinte a substitui, então percorrer os itens nunca soterra o botão voltar. Guarde o controlador para ler value/position e para conduzi-lo por código: set(position) abre, set(null) fecha, e set é exatamente a mesma escrita de baixo nível que o overlay usa internamente ao trocar de slide.

tsx
import { useOverlayUrlState, urlIndexKey } from '@reelkit/react';
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';

const photo = useOverlayUrlState({
  param: 'photo',
  ...urlIndexKey(() => images.length),
});

// Opening is a link — the overlay reads the URL and opens itself.
<Link to="?photo=3"><img src={images[3].src} /></Link>
<LightboxUrlOverlay controller={photo} images={images} />

// Read the url-derived state, or close programmatically (a low-level write).
photo.position.value; // 3 for ?photo=3, null when nothing is open
photo.set(null); // close

// Routed app: pass a router-backed adapter, otherwise the router's
// own location goes stale and its next navigation drops the param.
const adapter = useReactRouterUrlAdapter();
const routed = useOverlayUrlState({
  param: 'photo',
  adapter,
  ...urlIndexKey(() => images.length),
});

O objeto de opções recebe param, codec e locator (os três obrigatórios), mais um adapter opcional. param e adapter são lidos na primeira renderização e ficam fixos depois disso — remonte o componente para trocá-los —, enquanto codec e locator são lidos ao vivo, então uma busca feita depois de a lista crescer ou mudar de ordem enxerga a lista atual. O codec e o locator formam um par combinado que divide o mesmo Id, por isso andam juntos — em uma galeria simples com ?photo=3, espalhe ...urlIndexKey(() => images.length), que devolve as duas metades de uma vez. urlIndexKey liga o parâmetro a um índice de slide e o limita pela contagem atual que o getter devolve, então um ?photo=99 velho ou fora do intervalo é rejeitado e se cura sozinho saindo da URL, em vez de abrir um slide que ninguém nomeou. Passe um getter, e não um número, para que o limite continue certo à medida que um feed paginado cresce. Por baixo, ele embrulha createIndexLocator (a metade do locator) e a combina com indexCodec. Um feed paginado ou uma galeria endereçada por identidade fornece o próprio par codec + locator. A tabela completa de opções está na referência da API para React.

ReelIndicator

Componente opcional que mostra indicadores de progresso no estilo Instagram, sinalizando a posição atual dentro do slider. Colocado dentro de um Reel, ele se liga sozinho aos valores de count e active do componente pai pelo contexto — sem estado ligado à mão.

tsx
import { Reel, ReelIndicator } from '@reelkit/react';

{/* Auto-connect: count and active are inherited from parent Reel */}
<Reel count={10} size={[400, 600]} itemBuilder={...}>
  <ReelIndicator />
</Reel>

{/* Manual usage: pass count and active explicitly (e.g. outside a Reel) */}
<ReelIndicator count={10} active={currentIndex} />

Pontos principais

  • prop size

    Tupla [largura, altura] opcional; sem ela, o tamanho vem do CSS

  • itemBuilder

    Recebe o índice e devolve o conteúdo do slide

  • apiRef

    Dá acesso aos métodos do controlador para navegar

  • afterChange

    Acompanha o índice atual para atualizar a interface

Dicas de desempenho

  • Memorize os arrays de dados

    Envolva seu array de itens com useMemo. Uma nova referência de array a cada renderização dispara uma atualização de count e o recálculo dos intervalos visíveis.

  • Deixe o itemBuilder leve

    Ele roda a cada mudança do intervalo visível (normalmente 3 slides). Evite cálculos pesados ou efeitos colaterais lá dentro.

  • Carregue os dados perto da borda

    Use afterChange para perceber quando o leitor se aproxima do fim e busque o lote seguinte antes que os slides acabem (veja a receita Feed infinito).

  • Desligue a roda em páginas roláveis

    Defina enableWheel={false} quando o slider estiver dentro de um layout rolável, para não capturar a rolagem da página.

Próximos passos