
Pagiflow is a vanilla JavaScript carousel slider library for creating responsive carousels, touch-enabled sliders, gallery grids, thumbnail galleries, and fade-based slide views.
It runs on modern browsers with ES6+ class-based internals, GPU-accelerated CSS transforms, and a clean factory function API that works on any vanilla HTML project.
Features:
- Horizontal, vertical, grid, and fade transition modes.
- Touch and swipe detection.
- Thumbnail strip navigation with top, bottom, left, and right positioning.
- Per-breakpoint configuration through a responsive settings map.
- Continuous marquee auto-scroll mode for announcement or logo strips.
- Infinite loop playback through a clone-based DOM technique.
- Lazy loading for offscreen slide images via IntersectionObserver.
- Synchronized dual-slider linking for thumbnail-and-preview setups.
- Keyboard arrow and Home/End key navigation for accessibility.
- Right-to-left layout support.
- Center mode with configurable side padding for peeking adjacent slides.
- Pagination dot controls with custom absolute positioning.
- External navigation button support for fully custom UI designs.
How to use it:
1. Add the stylesheet in the document head and load the script before the closing body tag:
<link rel="stylesheet" href="/dist/css/pagiflow.min.css" /> <script src="/dist/js/pagiflow.min.js"></script>
2. Pagiflow wraps each direct child of your target element as a slide. The minimum required markup is a container with child elements.
<!-- Each direct child becomes one slide --> <div id="featuredSlider"> <div><img src="product-a.jpg" alt="Product A" /></div> <div><img src="product-b.jpg" alt="Product B" /></div> <div><img src="product-c.jpg" alt="Product C" /></div> </div>
3. Initialize Pagiflow with selector and options object:
const slider = Pagiflow('#featuredSlider', {
itemsPerSlide: 1, // Show one slide at a time
nav: true, // Render built-in prev/next buttons
paginate: true, // Show pagination dots below the slider
autoplay: true, // Auto-advance slides
autoplayDelay: 5000, // Wait 5 seconds between advances
loop: true, // Wrap around at start and end
speed: 350, // Transition duration in milliseconds
});4. This example shows how to display multiple items per slide:
// Show 3 slides at once with responsive overrides
const gallerySlider = Pagiflow('#gallerySlider', {
itemsPerSlide: 3, // Default: show 3 per view
slidesToScroll: 1, // Advance one slide per navigation click
gap: 16, // 16px gap between slides
nav: true,
loop: true,
responsive: {
// At viewports smaller than 768px, show 2 slides
768: {
itemsPerSlide: 2,
},
// At viewports smaller than 480px, show 1 slide
480: {
itemsPerSlide: 1,
},
},
});5. Create synced sliders:
// Initialize the primary slider first
const mainSlider = Pagiflow('#mainView', {
itemsPerSlide: 1,
nav: true,
loop: true,
});
// Sync a secondary slider to the first instance
const thumbSlider = Pagiflow('#thumbTrack', {
itemsPerSlide: 4,
sync: mainSlider, // Pass the instance directly
loop: true,
});6. All configuration options:
direction(string): Slide axis for the transition. Accepts'horizontal'or'vertical'. Default:'horizontal'.itemsPerSlide(number): Number of slides visible at once in the viewport. Default:1.slidesToScroll(number): Number of slides to advance per navigation click. Default:1.gap(number): Pixel gap between adjacent slide elements. Default:8.height(string | number): Viewport height used in vertical and fade modes. Accepts px or vh values. Default:'30vh'.startIndex(number): Zero-based index of the slide to show on first render. Default:0.loop(boolean): Wrap from the last slide back to the first. Uses DOM clones internally. Default:false.speed(number): CSS transition duration in milliseconds. Default:400.animate(boolean): Activate CSS transitions. Set tofalsefor instant jumps. Default:true.fade(boolean): Use crossfade instead of translate. Active only in horizontal mode with one item per slide. Default:false.nav(boolean): Render built-in previous and next navigation buttons. Default:false.navDisabledEnd(boolean): Disable (not hide) navigation buttons when at the first or last slide. Default:false.navigation(object | null): Connect external HTML buttons. Pass{ prev: '.my-prev', next: '.my-next' }. Default:null.prevIcon(string): HTML string for the previous button label. Default:'‹'.nextIcon(string): HTML string for the next button label. Default:'›'.prevPosition(string | object | null): Absolute position for the previous button. Pass a CSS class string or a{ top, left, right, bottom, transform }object. Default:null.nextPosition(string | object | null): Absolute position for the next button. Same format asprevPosition. Default:null.paginate(boolean): Render pagination dot controls below the slider. Default:false.paginationPosition(string | object | null): Absolute position override for the pagination element. Default:null.autoplay(boolean): Auto-advance slides on a timed interval. Default:false.autoplayDelay(number): Milliseconds between auto-advances. Default:3000.pauseOnHover(boolean): Pause autoplay when the pointer enters the slider wrapper. Default:true.autoScroll(boolean): Activate continuous marquee scrolling mode. Disables snap navigation. Default:false.autoScrollSpeed(number): Pixels moved perrequestAnimationFramecall in auto-scroll mode. Default:0.5.autoScrollDirection(string): Scroll direction in marquee mode. Accepts'left'or'right'. Default:'left'.swipeThreshold(number): Minimum swipe distance in pixels to trigger a slide change. Default:60.grid(boolean): Group items into a multi-row grid layout inside each slide page. Default:false.gridColumns(number): Column count when grid mode is active. Default:2.gridFill(boolean): Fill the last grid page by recycling earlier items as decorative placeholders. Default:false.thumbnails.enabled(boolean): Render a thumbnail strip synced to the active slide. Default:false.thumbnails.position(string): Thumbnail strip placement. Accepts'top','bottom','left', or'right'. Default:'bottom'.thumbnails.thumbWidth(number): Width of each thumbnail button in pixels. Default:80.thumbnails.gap(number): Gap between thumbnail items in pixels. Default:5.thumbnails.sidebarHeight(string | null): Explicit height for the thumbnail sidebar in left/right mode. Default:null.centerMode(boolean): Center the active slide and partially reveal its neighbors. Default:false.centerPadding(number | string): Side padding for center mode in pixels or CSS string. Default:0.rtl(boolean): Reverse the slide direction for right-to-left layouts. Default:false.keyboard(boolean): Activate arrow key and Home/End keyboard navigation. Default:false.lazyLoad(boolean): Defer loading ofimgelements in offscreen slides. Uses IntersectionObserver when available. Default:false.sync(string | instance | null): Selector string or Pagiflow instance to keep in sync with this slider. Default:null.responsive(object): Map of minimum viewport widths to option overrides. Breakpoints merge upward at each resize. Default:{}.html(string | array | null): Inject slide HTML during initialization. Pass a raw HTML string or an array of HTML strings. Default:null.slider(string | null): CSS selector for the track element when passing a single configuration object toPagiflow(). Default:null.
7. API methods:
// Advance to the next slide
slider.next();
// Go back to the previous slide
slider.prev();
// Jump to a specific slide by zero-based index
slider.goTo(2);
// Jump silently with no transition animation
slider.goTo(2, { instant: true });
// Start or resume autoplay programmatically
slider.play();
// Stop autoplay and auto-scroll immediately
slider.pause();
// Resume after a pause call
slider.resume();
// Toggle between play and pause states
slider.togglePlayPause();
// Update one or more options at runtime
// Structural changes (itemsPerSlide, grid, etc.) trigger a rebuild automatically
slider.setOptions({
autoplayDelay: 3000,
pauseOnHover: false,
});
// Get the current slide HTML as a string
const currentHTML = slider.html();
// Replace all slide content at runtime with a new HTML string
slider.html('<div>New Slide A</div><div>New Slide B</div>');
// Register a callback that fires each time the active slide changes
// Receives the real zero-based index of the newly active slide
slider.onSlideChange(function (index) {
console.log('Active slide index:', index);
});
// Fully remove the slider, clean up all event listeners, and restore the original DOM
slider.destroy();
// Re-initialize a destroyed instance with optional new options
slider.reInit({ autoplay: true, loop: true });FAQs:
Q: Can I use Pagiflow inside a React or Vue component?
A: Yes. Initialize the slider inside useEffect (React) or mounted (Vue) after the DOM is ready. Call slider.destroy() in the cleanup function or beforeUnmount hook to avoid memory leaks.
Q: My loop slider jumps visibly on the first and last slide. How do I fix it?
A: This usually means the transitionend event is firing late. Check that no CSS rule overrides the transition on .pagiflow-track from outside the library. A transition: none !important rule on the parent is a common culprit.
Q: Can I update itemsPerSlide after the slider is already running?
A: Yes. Call slider.setOptions({ itemsPerSlide: 4 }). Pagiflow detects structural option changes and triggers a full rebuild automatically.
Q: Does fade mode work with multiple visible slides?
A: No. Fade mode activates only when itemsPerSlide is set to 1, grid is false, autoScroll is false, and direction is 'horizontal'. Set fade: true with any other configuration and the library falls back to the standard translate transition.






