# Reelkit
> Headless, virtualized, TikTok-style vertical slider component library for React, Vue, and Angular. Zero dependencies in core; renders only 3 slides to the DOM at any time via virtualization.
This file indexes the Reelkit documentation so LLM agents can consume it without scraping the React-rendered site. Each link points at the public docs URL; fetch `llms-full.txt` for the same index with embedded per-page summaries.
Each section below embeds the full hand-authored summary for every page — fetch this file instead of `llms.txt` when you want content inline.
## Getting started
### Getting Started
URL: https://reelkit.dev/docs/getting-started
# Getting Started
reelkit = **single-item slider**. One item visible at time. Like TikTok, Instagram Reels, Stories. Good for vertical video feeds, fullscreen galleries, swipeable content.
> [!WARNING]
> ReelKit under active dev. While 0.x.x, APIs may change between minor versions, no deprecation period. Pin version to avoid breakage.
## Try It Online
Try in browser, no install:
- React Demo: https://react-demo.reelkit.dev/?utm_source=docs
- React Starter (StackBlitz): https://stackblitz.com/github/KonstantinKai/reelkit-react-starter
- Angular Demo: https://angular-demo.reelkit.dev/?utm_source=docs
- Angular Starter (StackBlitz): https://stackblitz.com/github/KonstantinKai/reelkit-angular-starter
- Vue Demo: https://vue-demo.reelkit.dev/?utm_source=docs
- Vue Starter (StackBlitz): https://stackblitz.com/github/KonstantinKai/reelkit-vue-starter
## Quick Start
Minimal vertical slider.
### React
```tsx
import { Reel, ReelIndicator } from '@reelkit/react';
const items = [
{ id: 1, title: 'Slide 1', color: '#6366f1' },
{ id: 2, title: 'Slide 2', color: '#8b5cf6' },
{ id: 3, title: 'Slide 3', color: '#ec4899' },
];
function App() {
return (
(
```
## Key Concepts
### Reel
`Reel` = main container. Manages slider state, handles touch gestures, keyboard nav, animations. Render prop pattern via `itemBuilder` for slides.
### itemBuilder
`itemBuilder` prop = function. Gets index, returns slide content. Enables virtualization — only visible items render.
### ReelIndicator
Optional. Instagram-style progress indicators. Shows current position.
### React example
```tsx
import { Reel, ReelIndicator } from '@reelkit/react';
function App() {
return (
}
>
);
}
```
### Angular example
```html
```
### Vue example
```vue-html
```
## Navigation
Built-in nav methods:
- **Touch/Swipe:** Drag to navigate. Momentum + snap.
- **Keyboard:** Arrow keys. Overlay components also handle Escape to close.
- **Mouse Wheel:** Enable with `enableWheel` prop.
- **Programmatic:** Use `apiRef` for `next()`, `prev()`, `goTo()`.
### React
```tsx
import { useRef } from 'react';
import { Reel, type ReelApi } from '@reelkit/react';
function App() {
const apiRef = useRef(null);
return (
<>
}
/>
>
);
}
```
### Angular
```html
```
### Vue
```vue
```
## Next Steps
- [Installation](/docs/installation) — all packages + setup options
- [Core Guide](/docs/core/guide) — framework-agnostic engine
- Framework Guide — React: [/docs/react/guide](/docs/react/guide), Angular: [/docs/angular/guide](/docs/angular/guide), Vue: [/docs/vue/guide](/docs/vue/guide)
- Reel Player — React: [/docs/reel-player](/docs/reel-player), Angular: [/docs/angular-reel-player](/docs/angular-reel-player), Vue: [/docs/vue-reel-player](/docs/vue-reel-player)
- Lightbox — React: [/docs/lightbox](/docs/lightbox), Angular: [/docs/angular-lightbox](/docs/angular-lightbox), Vue: [/docs/vue-lightbox](/docs/vue-lightbox)
- Stories Player — React: [/docs/stories-player](/docs/stories-player), Angular: [/docs/angular-stories-player](/docs/angular-stories-player), Vue: [/docs/vue-stories-player](/docs/vue-stories-player)
### Installation
URL: https://reelkit.dev/docs/installation
# Installation
Install reelkit packages + configure project. Each package version independent — install only what use.
## Package Options
| Package | Description | Use Case |
| ------------------------------- | -------------------------------- | ------------------------------------ |
| `@reelkit/core` | Framework-agnostic core | Custom integrations |
| `@reelkit/react` | React components | React 18+ applications |
| `@reelkit/react-reel-player` | Full-screen vertical reel player | Instagram/TikTok style player |
| `@reelkit/react-lightbox` | Image gallery lightbox | Full-screen image preview |
| `@reelkit/react-stories-player` | Instagram-style stories player | Stories with auto-advance + gestures |
| `@reelkit/angular` | Angular standalone components | Angular 17+ applications |
| `@reelkit/angular-reel-player` | Full-screen vertical reel player | Instagram/TikTok style player |
| `@reelkit/angular-lightbox` | Image gallery lightbox | Full-screen image preview |
| `@reelkit/vue` | Vue 3 components and composables | Vue 3 applications |
| `@reelkit/vue-reel-player` | Full-screen vertical reel player | Instagram/TikTok style player |
| `@reelkit/vue-lightbox` | Image gallery lightbox | Full-screen image preview |
## Install Commands
### React
```bash
npm install @reelkit/react
yarn add @reelkit/react
pnpm add @reelkit/react
```
### Angular
```bash
npm install @reelkit/angular
yarn add @reelkit/angular
pnpm add @reelkit/angular
```
### Vue
```bash
npm install @reelkit/vue
yarn add @reelkit/vue
pnpm add @reelkit/vue
```
## Peer Dependencies
### React
`@reelkit/react`:
- `react` >= 18.0.0
- `react-dom` >= 18.0.0
`@reelkit/react-reel-player`:
- `@reelkit/react`
- `react` >= 18.0.0
- `react-dom` >= 18.0.0
- `lucide-react` >= 0.400.0
`@reelkit/react-lightbox`:
- `@reelkit/react`
- `react` >= 18.0.0
- `react-dom` >= 18.0.0
- `lucide-react` >= 0.400.0
`@reelkit/react-stories-player`:
- `@reelkit/react`
- `react` >= 18.0.0
- `react-dom` >= 18.0.0
- `lucide-react` >= 0.400.0
`lucide-react` only needed for default control icons. Supply own controls via `renderControls` = skip install.
### Angular
`@reelkit/angular`:
- `@angular/core` >= 17.0.0
- `@angular/common` >= 17.0.0
`@reelkit/angular-reel-player`:
- `@reelkit/angular`
- `@angular/core` >= 19.0.0
- `lucide-angular` >= 0.460.0
`@reelkit/angular-lightbox`:
- `@reelkit/angular`
- `@angular/core` >= 17.0.0
- `lucide-angular` >= 0.400.0
`lucide-angular` only needed for default control icons. Supply own controls via `rkPlayerControls` = skip install.
### Vue
`@reelkit/vue`:
- `vue` >= 3.0.0
## TypeScript
All packages ship TypeScript types. No `@types` packages needed.
## Browser Support
reelkit support all modern browsers:
- Chrome/Edge 88+
- Firefox 78+
- Safari 14+
- iOS Safari 14+
- Android Chrome 88+
### Server-Side Rendering
URL: https://reelkit.dev/docs/ssr
# Server-Side Rendering
All reelkit packages work on server. Import + render with Next.js, Remix, Angular Universal, Nuxt 3, any SSR setup.
## How It Works
Core slider controller pure logic — no DOM access at construction. Gesture, keyboard, wheel listeners attach only in client lifecycle hooks.
During SSR, `Reel` renders static container with initial visible slides (typically 3: prev, current, next). On hydration, attaches gesture/keyboard/wheel controllers → interactive.
### SSR Safety Matrix
| Package | SSR Safe | Notes |
| ------------------------------- | -------- | ------------------------------------------------------------ |
| `@reelkit/core` | yes | Pure logic, no browser APIs at import or construction |
| `@reelkit/react` | yes | `Reel` and `ReelIndicator` render valid HTML on server |
| `@reelkit/react-reel-player` | yes | Renders nothing when `isOpen=false` |
| `@reelkit/react-lightbox` | yes | Renders nothing when `isOpen=false` |
| `@reelkit/react-stories-player` | yes | Renders nothing when `isOpen=false` |
| `@reelkit/angular` | yes | Standalone components, SSR compatible with Angular Universal |
| `@reelkit/angular-reel-player` | yes | Renders nothing when `isOpen=false` |
| `@reelkit/angular-lightbox` | yes | Renders nothing when `isOpen=false` |
| `@reelkit/vue` | yes | Components and composables, SSR compatible with Nuxt 3 |
| `@reelkit/stories-core` | yes | Framework-agnostic, no DOM access |
## React
### Next.js App Router
`Reel` use browser events + refs → Client Component. Add `"use client"` at top:
```tsx
'use client';
import { Reel, ReelIndicator } from '@reelkit/react';
export function Feed({ items }: { items: FeedItem[] }) {
return (
(
{items[index].title}
)}
>
);
}
```
Fetch data in Server Component, pass down:
```tsx
// app/feed/page.tsx (Server Component)
import { Feed } from './Feed';
export default async function FeedPage() {
const items = await fetchFeedItems();
return ;
}
```
### Next.js Pages Router
Works no extra config. Renders during SSR, hydrates on client:
```tsx
// pages/feed.tsx
import { Reel } from '@reelkit/react';
import type { GetServerSideProps } from 'next';
interface Props {
items: FeedItem[];
}
export const getServerSideProps: GetServerSideProps = async () => {
const items = await fetchFeedItems();
return { props: { items } };
};
export default function FeedPage({ items }: Props) {
return (
}
/>
);
}
```
### Responsive Size with SSR (React)
Omit `size` prop. When not provided, `Reel` auto-measures container via `ResizeObserver` on client. SSR renders empty container; hydration measures + renders slides immediately:
```tsx
'use client';
import { Reel } from '@reelkit/react';
export function FullScreenFeed({ items }: { items: FeedItem[] }) {
return (
}
/>
);
}
```
When `size` omitted, container ! sized by CSS (parent flex/grid, explicit width/height, or percentages). Slider renders nothing until first measurement, then fills measured dimensions + responds to resizes auto.
#### Explicit size (manual approach)
For pixel control, pass explicit `size`. Since `window.innerWidth` no available during SSR, provide default + update on mount:
```tsx
'use client';
import { useState, useEffect } from 'react';
import { Reel } from '@reelkit/react';
// Default size for SSR — matches common mobile viewport
const DEFAULT_SIZE: [number, number] = [390, 844];
export function FullScreenFeed({ items }: { items: FeedItem[] }) {
const [size, setSize] = useState<[number, number]>(DEFAULT_SIZE);
useEffect(() => {
const update = () => setSize([window.innerWidth, window.innerHeight]);
update();
window.addEventListener('resize', update);
return () => window.removeEventListener('resize', update);
}, []);
return (
}
/>
);
}
```
### Overlay Components (React)
`ReelPlayerOverlay` + `LightboxOverlay` render nothing when `isOpen={false}` → SSR-safe by default. Mount portal only when opened (typically from user interaction on client):
```tsx
'use client';
import { useState } from 'react';
import { ReelPlayerOverlay } from '@reelkit/react-reel-player';
export function VideoFeed({ content }: { content: ContentItem[] }) {
const [isOpen, setIsOpen] = useState(false);
const [startIndex, setStartIndex] = useState(0);
return (
<>
{content.map((item, i) => (
))}
setIsOpen(false)}
content={content}
initialIndex={startIndex}
/>
>
);
}
```
## Angular
### Angular Universal / SSR
All Angular components SSR-safe. Slider controller defers browser API access to `afterRenderEffect`. Overlay components render nothing when `isOpen=false` → no markup during server render.
```typescript
import { Component, signal } from '@angular/core';
import { RkReelPlayerOverlayComponent } from '@reelkit/angular-reel-player';
@Component({
selector: 'app-feed',
standalone: true,
imports: [RkReelPlayerOverlayComponent],
template: `
`,
})
export class FeedComponent {
isOpen = signal(false);
content = [
/* ... */
];
}
```
## Vue
### Nuxt 3
ReelKit Vue works with Nuxt 3 out of box. `Reel` use browser APIs (touch events, ResizeObserver) → wrap in `` or use `.client.vue` suffix:
```vue
```
Feed uses Reel normally:
```vue
{{ items[index].title }}
```
### Responsive Size with SSR (Vue)
Omit `size` for auto-measurement. Reel auto-sizes to 100% of parent. SSR renders empty container; hydration measures + renders slides:
```vue
```
## Using Core Directly
When using `@reelkit/core` direct for custom framework integration, create controller on server. Call `attach()` + `observe()` on client:
```typescript
import { createSliderController } from '@reelkit/core';
// Safe to call on the server — no DOM access
const controller = createSliderController({
count: 10,
direction: 'vertical',
});
// Only call on the client — attaches DOM event listeners
if (typeof window !== 'undefined') {
controller.attach(element);
controller.observe();
}
```
## Summary
> [!SUCCESS]
> Works out of box:
>
> - Import any reelkit package on server
> - Render slider components during SSR (valid static HTML)
> - Create controllers on server
> - Overlay components when `isOpen=false`
> [!WARNING]
> Keep in mind:
>
> - Omit `size` for auto-measurement, or provide default when using viewport-based dimensions
> - Don't call `attach()` / `observe()` on server when using core direct
## Core
### Core Guide
URL: https://reelkit.dev/docs/core/guide
# Core Guide
`@reelkit/core` = framework-agnostic slider logic. Build custom integrations or understand architecture.
## Architecture Overview
Core use **controller pattern** with factory functions. No classes — all plain objects from closures. Zero deps. Core coordinates:
- **SliderController** — central state + navigation
- **GestureController** — touch/pointer drag
- **KeyboardController** — arrow keys + Escape
- **WheelController** — mouse wheel, debounced
## createSliderController
Make new slider controller instance. Manages all slider state + behavior.
```typescript
import { createSliderController } from '@reelkit/core';
const controller = createSliderController(
{
count: 10,
direction: 'vertical',
enableWheel: true,
transitionDuration: 300,
},
{
onAfterChange: (index) => console.log('Changed to:', index),
},
);
// Attach to DOM element
controller.attach(element);
controller.observe();
```
## Controller Methods
### Navigation
```typescript
// Go to specific index
controller.goTo(5); // instant
controller.goTo(5, true); // animated, returns Promise
// Navigate to next/previous
controller.next();
controller.prev();
```
### Lifecycle
```typescript
// Connect to DOM element
controller.attach(element);
// Start gesture, keyboard, and wheel observation
controller.observe();
// Stop gesture, keyboard, and wheel observation
controller.unobserve();
// Detach DOM listeners (reversible — use for React effect cleanup)
controller.detach();
// Permanent teardown (use for Angular onDestroy)
controller.dispose();
// Recalculate positions
controller.adjust();
// Update size
controller.setPrimarySize(600);
```
### State Updates
```typescript
// Update configuration
controller.updateConfig({
count: 20,
loop: true,
});
```
## Virtualization
Core render only **3 slides to DOM** at any time (current, previous, next). Range extractor pick which indices in rendered window:
```typescript
import { defaultRangeExtractor } from '@reelkit/core';
// Default: renders current ± 1 (3 DOM nodes)
const indexes = defaultRangeExtractor(currentIndex, count);
// Custom: skip hidden slides by shifting to next valid index
const hiddenSlides = new Set([2, 5]);
const skipHiddenExtractor = (current: number, count: number) => {
const result: number[] = [];
// Collect prev, current, next — skip hidden, shift forward
for (let i = current - 1, added = 0; added < 3 && i < count; i++) {
if (i >= 0 && !hiddenSlides.has(i)) {
result.push(i);
added++;
}
}
return result;
};
```
Result always clamped max 3 indices. If extractor return more, core keep 3 centered on current slide.
## Signals
Core use lightweight signal system for reactivity:
```typescript
import { createSignal, createComputed, reaction } from '@reelkit/core';
// Create a signal
const count = createSignal(0);
// Observe changes (returns a disposer function)
const dispose = count.observe(() => console.log(count.value));
// Update value
count.value = 5;
// Create computed signal (requires a deps factory)
const doubled = createComputed(
() => count.value * 2,
() => [count],
);
// Run side effects on signal changes
const disposeReaction = reaction(
() => [count],
() => console.log('Count changed:', count.value),
);
// Cleanup
dispose();
disposeReaction();
```
## Controller State
Reach reactive state via `controller.state`:
```typescript
const { index, axisValue, indexes } = controller.state;
// Observe index changes (returns a disposer function)
const disposeIndex = index.observe(() => {
console.log('Current index:', index.value);
});
// Observe visible indexes for virtualization
const disposeIndexes = indexes.observe(() => {
console.log('Visible:', indexes.value);
});
// Cleanup when done
disposeIndex();
disposeIndexes();
```
## Timeline Controller
Build custom scrub bar for any `