Guía de Vue
Aprende a crear sliders con @reelkit/vue.
Slider básico
El componente <Reel> envuelve el controlador del slider del core. Usa el slot #item para renderizar cada slide con virtualización: solo se montan los slides visibles.
<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 muestra indicadores de progreso al estilo de Instagram con la posición actual en el slider. Colocado dentro de un <Reel>, se conecta solo a los valores count y active del padre a través del provide/inject de Vue, sin conectar nada a mano.
<!-- 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 — template ref
El componente <Reel> expone una interfaz ReelExpose a través de un template ref. Usa ref() para guardar la referencia y llamar a métodos imperativos como next(), prev() y 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>Dirección horizontal
Pon direction="horizontal" para un slider que se desliza a izquierda y derecha. La dirección del indicador debería coincidir.
<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>Tamaño automático
La prop size es opcional. Sin ella, <Reel> mide su contenedor con ResizeObserver y se adapta al diseño que marque el CSS. El tamaño del contenedor lo tiene que dar su padre (por ejemplo, flex, grid o dimensiones explícitas en 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>Transiciones
Pasa una prop transition para personalizar la animación de los slides. ReelKit incluye cinco transiciones que admiten tree-shaking: slideTransition (por defecto), fadeTransition, flipTransition, cubeTransition y 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 bucle
Activa la navegación circular infinita con la prop loop. El slider pasa sin cortes del último slide al primero (y al revés).
<Reel
:count="items.length"
:size="[400, 600]"
:loop="true"
>
<template #item="{ index, size }"> ... </template>
</Reel>Callbacks de eventos
El componente <Reel> emite varios eventos para seguir el estado del 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>Navegación
Métodos de navegación incluidos:
- Táctil / deslizar: arrastra para navegar con inercia y ajuste al slide
- Teclado: teclas de flecha y Escape
- Rueda del ratón: se activa con
:enable-wheel="true" - Por código: usa un template ref para acceder a
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 en la URL
useOverlayUrlState crea un controlador de estado en la URL para un overlay y lo devuelve entero; después se lo pasas a un <LightboxUrlOverlay> en su prop :controller. La barra de direcciones es dueña del estado abierto, así que un overlay vinculado se abre solo y lo habitual es abrirlo con un enlace. La primera escritura de un parámetro ausente añade una entrada al historial y las siguientes la sustituyen, así que pasar slides nunca entierra el botón de volver. Guarda el controlador para leer value/position y para cerrar por código con set(null), la misma escritura de bajo nivel que usa el overlay internamente al cambiar 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>El objeto de opciones recibe param, codec y locator (los tres obligatorios), más un adapter opcional. El codec y el locator son un par a juego que comparte el mismo Id, así que para una galería simple con ?photo=3 usa ...urlIndexKey(() => props.images.length), que devuelve las dos mitades a la vez. urlIndexKey convierte el parámetro en el índice de un slide y lo limita con el recuento actual que devuelve el getter, así que un ?photo=99 desfasado o fuera de rango se rechaza y se limpia solo de la URL en lugar de abrir un slide que nunca se nombró. Pasa un getter y no un número: el setup de Vue se ejecuta una sola vez, así que una longitud capturada quedaría desfasada mientras crece un feed paginado. Envuelve createIndexLocator (la mitad del locator) y lo combina con indexCodec. Un feed paginado o una galería por identidad aporta su propio par de codec + locator. La tabla completa de opciones está en la referencia de la API de Vue.
Patrón del slot #item
En lugar de la render prop de React, Vue usa el scoped slot #item. Esto hace posible la virtualización: solo se montan los slides visibles. El ámbito del slot ofrece tres propiedades:
<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 incluye composables para los casos de overlay más comunes:
<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>Puntos clave
- Composition API
Importa
Reel,ReelIndicatory los composables directamente en tu<script setup>, sin registrar ningún plugin - Scoped slot #item
El equivalente en Vue de la prop
itemBuilderde React: hace posible la virtualización con la sintaxis de plantillas de siempre - Template ref
Usa
ref<ReelExpose>()para navegar de forma imperativa, sin callbacks de eventos - @after-change
Emite
(index, rangeIndex): sigue el índice actual para actualizar la interfaz - Contexto con provide/inject
ReelIndicatorse conecta solo alReelpadre con el provide/inject de Vue, sin pasar props a mano por varios niveles
Consejos de rendimiento
- Mantén ligeras las plantillas de los slides
El slot
#itemse ejecuta para cada slide visible (normalmente 3 a la vez). Evita dentro cálculos pesados o estructuras muy anidadas. - Carga datos cerca del final
Usa
@after-changepara detectar cuándo el usuario se acerca al final y pide el siguiente lote antes de que se acaben los slides; así puedes tener feeds con scroll infinito. - Usa refs para el estado imperativo
Guarda la referencia a
ReelExposey el índice actual enref()de Vue para tener una reactividad precisa sin re-renderizados innecesarios. - Desactiva la rueda en páginas con scroll
Pon
:enable-wheel="false"cuando el slider esté dentro de un diseño con scroll para no capturar el desplazamiento de la página.
Siguientes pasos
- Referencia de la API de Vue - todas las props, eventos y composables
- Guía del core - el motor independiente del framework
- Reel Player - reproductor de vídeo al estilo de TikTok/ReelsReel Player - reproductor de vídeo al estilo de TikTok/ReelsReel Player - reproductor de vídeo al estilo de TikTok/Reels
- Lightbox - galería de imágenes y vídeosLightbox - galería de imágenes y vídeosLightbox - galería de imágenes y vídeos