Guia para Vue
Aprenda a montar sliders com @reelkit/vue.
Slider básico
O componente <Reel> embrulha o controlador do slider do core. Use o slot #item para desenhar cada slide com virtualização — só os slides visíveis são montados.
<script setup lang="ts">
import { Reel, ReelIndicator } from '@reelkit/vue';
const items = [
{ title: 'Virtualized', subtitle: 'Only 3 slides in DOM', color: '#6366f1' },
{ title: 'Touch First', subtitle: 'Native swipe gestures', color: '#8b5cf6' },
{ title: 'Zero Deps', subtitle: 'Tiny bundle size', color: '#7c3aed' },
{ title: 'Keyboard Nav', subtitle: 'Full a11y support', color: '#ec4899' },
{ title: 'SSR Ready', subtitle: 'Works everywhere', color: '#14b8a6' },
{ title: '60fps', subtitle: 'Smooth animations', color: '#f59e0b' },
];
const onAfterChange = (index: number) => {
console.log('Current index:', index);
};
</script>
<template>
<Reel
:count="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
:enable-wheel="true"
@after-change="onAfterChange"
>
<template #item="{ index, size }">
<div
:style="{
width: size[0] + 'px',
height: size[1] + 'px',
background: items[index].color,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
}"
>
<div style="font-size: 1.5rem; font-weight: bold">
{{ items[index].title }}
</div>
<div style="font-size: 0.875rem; opacity: 0.8">
{{ items[index].subtitle }}
</div>
</div>
</template>
<div style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); z-index: 10">
<ReelIndicator direction="vertical" />
</div>
</Reel>
</template>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 provide/inject do Vue — sem ligação manual.
<!-- Auto-connect: count and active are inherited from parent Reel -->
<Reel :count="10" :size="[400, 600]">
<template #item="{ index, size }"> ... </template>
<ReelIndicator direction="vertical" />
</Reel>
<!-- Manual usage: pass count and active explicitly (e.g. outside a Reel) -->
<ReelIndicator :count="10" :active="currentIndex" />API imperativa — ref de template
O componente <Reel> expõe a interface ReelExpose por um ref de template. Use ref() para guardar a referência e chamar métodos imperativos como next(), prev() e goTo().
<script setup lang="ts">
import { ref } from 'vue';
import { Reel, type ReelExpose } from '@reelkit/vue';
const items = [
{ title: 'Slide 1', color: '#6366f1' },
{ title: 'Slide 2', color: '#8b5cf6' },
{ title: 'Slide 3', color: '#ec4899' },
];
const sliderRef = ref<ReelExpose | null>(null);
const currentIndex = ref(0);
const onAfterChange = (index: number) => {
currentIndex.value = index;
};
</script>
<template>
<Reel
ref="sliderRef"
:count="items.length"
style="width: 100%; height: 100dvh"
direction="vertical"
:enable-wheel="true"
@after-change="onAfterChange"
>
<template #item="{ index, size }">
<div
:style="{
width: size[0] + 'px',
height: size[1] + 'px',
}"
>
{{ items[index].title }}
</div>
</template>
</Reel>
<div style="position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%)">
<button
:disabled="currentIndex === 0"
@click="sliderRef?.prev()"
>
Prev
</button>
<button
:disabled="currentIndex === items.length - 1"
@click="sliderRef?.next()"
>
Next
</button>
<button @click="sliderRef?.goTo(2)">Go to 3</button>
</div>
</template>Direção horizontal
Defina direction="horizontal" para um slider que desliza para os lados. A direção do indicador deve acompanhar.
<Reel
:count="items.length"
:size="[400, 300]"
direction="horizontal"
>
<template #item="{ index, size }">
<div :style="{ width: size[0] + 'px', height: size[1] + 'px' }">
{{ items[index].title }}
</div>
</template>
<ReelIndicator direction="horizontal" />
</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).
<!-- Explicit size (fixed) -->
<Reel :count="items.length" :size="[400, 600]">
<template #item="{ index, size }"> ... </template>
</Reel>
<!-- Auto-size (responsive — sized by CSS) -->
<Reel :count="items.length" style="width: 100%; height: 100dvh">
<template #item="{ index, size }"> ... </template>
</Reel>Transições
Passe a prop transition para personalizar a animação entre os slides. O ReelKit traz cinco transições que saem no tree-shaking: slideTransition (padrão), fadeTransition, flipTransition, cubeTransition e zoomTransition.
<script setup lang="ts">
import { Reel, cubeTransition } from '@reelkit/vue';
</script>
<template>
<Reel
:count="items.length"
:size="[400, 600]"
:transition="cubeTransition"
>
<template #item="{ index, size }"> ... </template>
</Reel>
</template>Modo cíclico
Ligue a navegação circular sem fim com a prop loop. O slider passa do último slide ao primeiro sem emenda (e vice-versa).
<Reel
:count="items.length"
:size="[400, 600]"
:loop="true"
>
<template #item="{ index, size }"> ... </template>
</Reel>Callbacks de eventos
O componente <Reel> emite vários eventos para você acompanhar o estado do slider:
<script setup lang="ts">
import { Reel } from '@reelkit/vue';
const onBeforeChange = (index: number, nextIndex: number, rangeIndex: number) => {
console.log('Transitioning from', index, 'to', nextIndex);
};
const onAfterChange = (index: number, rangeIndex: number) => {
console.log('Arrived at slide', index);
};
const onSlideDragStart = (index: number) => {
console.log('Started dragging slide', index);
};
const onSlideDragEnd = (index: number) => {
console.log('Stopped dragging slide', index);
};
</script>
<template>
<Reel
:count="20"
style="width: 100%; height: 100dvh"
@before-change="onBeforeChange"
@after-change="onAfterChange"
@slide-drag-start="onSlideDragStart"
@slide-drag-end="onSlideDragEnd"
>
<template #item="{ index, size }"> ... </template>
</Reel>
</template>Navegação
Formas de navegação já incluídas:
- Toque/Deslizar: arraste para navegar, com inércia e encaixe
- Teclado: setas e Escape
- Roda do mouse: ligue com
:enable-wheel="true" - Programática: use um ref de template para alcançar
next(),prev(),goTo()
<script setup lang="ts">
import { ref } from 'vue';
import { Reel, type ReelExpose } from '@reelkit/vue';
const sliderRef = ref<ReelExpose | null>(null);
</script>
<template>
<Reel
ref="sliderRef"
:count="10"
:size="[400, 600]"
>
<template #item="{ index, size }">
<div :style="{ width: size[0] + 'px', height: size[1] + 'px' }">
Slide {{ index + 1 }}
</div>
</template>
</Reel>
<button @click="sliderRef?.prev()">Prev</button>
<button @click="sliderRef?.next()">Next</button>
<button @click="sliderRef?.goTo(5)">Go to 6</button>
</template>Estado na URL
useOverlayUrlState monta um controlador de estado na URL para o overlay e devolve o controlador inteiro; você o entrega a um <LightboxUrlOverlay> pela prop :controller. Quem manda no estado aberto é a barra de endereços, 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 fechar por código com set(null), a mesma escrita de baixo nível que o overlay usa internamente ao trocar de slide.
<script setup lang="ts">
import { LightboxUrlOverlay, type LightboxItem } from '@reelkit/vue-lightbox';
import { useOverlayUrlState, urlIndexKey } from '@reelkit/vue';
const props = defineProps<{ images: LightboxItem[] }>();
const photo = useOverlayUrlState({
param: 'photo',
...urlIndexKey(() => props.images.length),
});
</script>
<template>
<!-- Opening is a link — the overlay reads the URL and opens itself. -->
<RouterLink v-for="(img, i) in props.images" :key="img.src" :to="`?photo=${i}`">
<img :src="img.src" />
</RouterLink>
<LightboxUrlOverlay :controller="photo" :items="props.images" />
</template>O objeto de opções recebe param, codec e locator (os três obrigatórios), mais um adapter opcional. O codec e o locator formam um par combinado que divide o mesmo Id, então em uma galeria simples com ?photo=3 espalhe ...urlIndexKey(() => props.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: o setup do Vue roda uma única vez, então um comprimento capturado envelheceria conforme o 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 Vue.
Padrão do slot #item
No lugar da render prop do React, o Vue usa o slot com escopo #item. É ele que viabiliza a virtualização — só os slides visíveis são montados. O escopo do slot entrega três propriedades:
<template #item="{ index, indexInRange, size }">
<!--
index : number — absolute slide index (0 to count-1)
indexInRange : number — position in visible window (0, 1, or 2)
size : [number, number] — [width, height] of the container
-->
<MySlide
:data="items[index]"
:style="{ width: size[0] + 'px', height: size[1] + 'px' }"
/>
</template>Composables
@reelkit/vue traz composables para os cenários comuns de overlay:
<script setup lang="ts">
import { ref } from 'vue';
import { useBodyLock, useFullscreen, useReelContext } from '@reelkit/vue';
// Lock body scroll when an overlay is open
const isOpen = ref(true);
useBodyLock(isOpen);
// Fullscreen API with cross-browser support
const containerRef = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen({ elementRef: containerRef });
// Access parent Reel context (when inside a Reel)
const reelContext = useReelContext();
// reelContext?.index — active slide index signal
// reelContext?.count — total slide count signal
// reelContext?.goTo() — navigate programmatically
// Bridge a core Subscribable into a reactive Vue ref
import { toVueRef } from '@reelkit/vue';
const index = toVueRef(reelContext!.index); // Ref<number> — re-renders on change
</script>Pontos principais
- Composition API
Importe
Reel,ReelIndicatore os composables direto no seu<script setup>— não é preciso registrar plugin nenhum - slot com escopo #item
O equivalente, no Vue, à prop
itemBuilderdo React — dá virtualização com a sintaxe de template de sempre - Ref de template
Use
ref<ReelExpose>()para navegar de forma imperativa — sem precisar de callbacks de evento - @after-change
Emite
(index, rangeIndex)— acompanhe o índice atual para atualizar a interface - contexto por provide/inject
O
ReelIndicatorse liga sozinho aoReelque o envolve pelo provide/inject do Vue — sem passar props de mão em mão
Dicas de desempenho
- Deixe os templates dos slides leves
O slot
#itemroda para cada slide visível (normalmente 3 por vez). Evite cálculos pesados ou estruturas muito aninhadas lá dentro. - Carregue os dados perto da borda
Use
@after-changepara perceber quando o leitor se aproxima do fim e busque o lote seguinte antes que os slides acabem — é assim que nasce um feed sem fim. - Use refs para o estado imperativo
Guarde a referência de
ReelExposee o índice atual emref()s do Vue, para ter reatividade fina sem renderizações desnecessárias. - Desligue a roda em páginas roláveis
Defina
:enable-wheel="false"quando o slider estiver dentro de um layout rolável, para não capturar a rolagem da página.
Próximos passos
- Referência da API para Vue - todas as props, eventos e composables
- Guia do core - o motor independente de framework
- Reel Player - player de vídeo no estilo TikTok/ReelsReel Player - player de vídeo no estilo TikTok/ReelsReel Player - player de vídeo no estilo TikTok/Reels
- Lightbox - galeria de imagens e vídeosLightbox - galeria de imagens e vídeosLightbox - galeria de imagens e vídeos