Reactive API
The Reactive API provides automatically synchronized state properties for Flicking, eliminating the need for manual event listeners and state management boilerplate.
Overview
When building carousel UIs, you often need to reflect Flicking's internal state in surrounding elements like pagination dots, progress bars, or navigation buttons. The traditional approach requires listening to multiple events and manually keeping your UI in sync.
The Reactive API solves this by exposing reactive state properties that update automatically whenever Flicking's state changes.
Traditional Approach vs Reactive API
- JavaScript
- React
- Vue3
Traditional (event-based):
const flicking = new Flicking("#el");
let progress = 0;
let isReachStart = true;
let isReachEnd = false;
flicking.on("move", () => {
const cam = flicking.camera;
progress = ((cam.position - cam.range.min) / (cam.range.max - cam.range.min)) * 100;
updateProgressBar(progress);
});
flicking.on("changed", () => {
isReachStart = flicking.index === 0;
isReachEnd = flicking.index === flicking.panelCount - 1;
updateButtons(isReachStart, isReachEnd);
});
Reactive API:
const flicking = new Flicking("#el");
const reactive = connectFlickingReactiveAPI(flicking);
reactive.subscribe("progress", (value) => updateProgressBar(value));
reactive.subscribe("isReachStart", (value) => updateButtons(value, reactive.isReachEnd));
reactive.subscribe("isReachEnd", (value) => updateButtons(reactive.isReachStart, value));
Traditional (event-based):
function App() {
const flickingRef = useRef(null);
const [progress, setProgress] = useState(0);
const [isReachStart, setIsReachStart] = useState(true);
const [isReachEnd, setIsReachEnd] = useState(false);
useEffect(() => {
const flicking = flickingRef.current;
if (!flicking) return;
const onMove = () => {
const cam = flicking.camera;
const ratio = (cam.position - cam.range.min) / (cam.range.max - cam.range.min);
setProgress(ratio * 100);
};
const onChanged = () => {
setIsReachStart(flicking.index === 0);
setIsReachEnd(flicking.index === flicking.panelCount - 1);
};
flicking.on("move", onMove);
flicking.on("changed", onChanged);
return () => {
flicking.off("move", onMove);
flicking.off("changed", onChanged);
};
}, []);
return (/* ... */);
}
Reactive API:
function App() {
const flickingRef = useRef(null);
const { progress, isReachStart, isReachEnd } = useFlickingReactiveAPI(flickingRef);
return (/* ... */);
}
Traditional (event-based):
<script setup>
const flickingRef = ref(null);
const progress = ref(0);
const isReachStart = ref(true);
const isReachEnd = ref(false);
const onMove = (e) => {
const cam = e.currentTarget.camera;
const ratio = (cam.position - cam.range.min) / (cam.range.max - cam.range.min);
progress.value = ratio * 100;
};
const onChanged = (e) => {
const flicking = e.currentTarget;
isReachStart.value = flicking.index === 0;
isReachEnd.value = flicking.index === flicking.panelCount - 1;
};
</script>
<template>
<Flicking ref="flickingRef" @move="onMove" @changed="onChanged">
<!-- panels -->
</Flicking>
</template>
Reactive API:
<script setup>
const flickingRef = ref(null);
const { progress, isReachStart, isReachEnd } = useFlickingReactiveAPI(flickingRef);
</script>
<template>
<Flicking ref="flickingRef">
<!-- panels -->
</Flicking>
</template>
Key Benefits
- Less boilerplate: No manual event listeners,
useState, oruseEffectto keep UI in sync. - Framework-native: Returns React state / Vue3 reactive refs that integrate naturally with your components and any UI libraries you use.
- Auto-sync: State updates are never missed. Drag, programmatic moves, and panel additions all trigger updates automatically.
- SSR-ready: Pre-set initial values to match server-rendered markup and avoid hydration flicker.
Getting Started
- JavaScript
- React
- Vue3
import Flicking, { connectFlickingReactiveAPI } from "@egjs/flicking";
const flicking = new Flicking("#el");
const reactive = connectFlickingReactiveAPI(flicking);
// Read current values
console.log(reactive.currentPanelIndex);
console.log(reactive.progress);
// Subscribe to changes
reactive.subscribe("currentPanelIndex", (value) => {
console.log("Panel changed to:", value);
});
// Use methods
reactive.moveTo(2);
connectFlickingReactiveAPI returns a reactive object. Use .subscribe() to listen for state changes.
import Flicking, { useFlickingReactiveAPI } from "@egjs/react-flicking";
function App() {
const flickingRef = useRef(null);
const {
currentPanelIndex,
totalPanelCount,
progress,
indexProgress,
isReachStart,
isReachEnd,
moveTo
} = useFlickingReactiveAPI(flickingRef);
return (
<Flicking ref={flickingRef}>
<div>Panel 1</div>
<div>Panel 2</div>
<div>Panel 3</div>
</Flicking>
);
}
useFlickingReactiveAPI is a React hook that returns reactive state values. When state changes, the component re-renders automatically.
<script setup>
import { ref } from "vue";
import Flicking, { useFlickingReactiveAPI } from "@egjs/vue3-flicking";
const flickingRef = ref(null);
const {
currentPanelIndex,
totalPanelCount,
progress,
indexProgress,
isReachStart,
isReachEnd,
moveTo
} = useFlickingReactiveAPI(flickingRef);
</script>
<template>
<Flicking ref="flickingRef">
<div>Panel 1</div>
<div>Panel 2</div>
<div>Panel 3</div>
</Flicking>
</template>
useFlickingReactiveAPI is a Vue3 composable that returns reactive refs. Templates update automatically when state changes.
Reactive State
| Property | Type | Description |
|---|---|---|
currentPanelIndex | number | Index of the currently active panel |
totalPanelCount | number | Total number of panels |
progress | number | Overall scroll progress as a percentage (0-100) |
indexProgress | number | Camera position as a fractional panel index (e.g., 2.5 means halfway between panel 2 and 3) |
isReachStart | boolean | Whether the first panel is currently active |
isReachEnd | boolean | Whether the last panel is currently active |
progressis best for continuous indicators like progress bars. It ranges from 0 to 100 and works well withmoveType: "freeScroll".indexProgressis best for per-panel visual effects like parallax or coverflow. It provides fractional values between panel indices and updates in real-time during drag.
Reactive Methods
| Method | Type | Description |
|---|---|---|
moveTo | (index: number) => Promise<void> | Move to a specific panel by index. Ignores calls while animating. |
SSR Optimization
When using SSR (Server-Side Rendering), you can provide initial values to prevent layout shifts during hydration:
- JavaScript
- React
- Vue3
const reactive = connectFlickingReactiveAPI(flicking, {
defaultIndex: 2,
totalPanelCount: 10
});
const { currentPanelIndex } = useFlickingReactiveAPI(flickingRef, {
defaultIndex: 2, // Start at panel 2
totalPanelCount: 10 // Pre-set panel count
});
const { currentPanelIndex } = useFlickingReactiveAPI(flickingRef, {
defaultIndex: 2,
totalPanelCount: 10
});
| Option | Type | Default | Description |
|---|---|---|---|
defaultIndex | number | 0 | Initial panel index. Also affects isReachStart, isReachEnd, and indexProgress initial values. |
totalPanelCount | number | 0 | Initial panel count. Prevents layout shifts when pagination UI depends on this value. |
Next Steps
- Demos: See Reactive API in action with interactive examples
- API Reference: Full type definitions and property details