full-view-snap-react
v1.0.12
Published
A React component for viewport vertical scroll snapping with integrated navigation.
Readme
full-view-snap-react
A React native root viewport scroll wrapper component for css scroll snapping with integrated navigation and reusable UI components.
Official website: full-view-snap.websultancy.com
Why full-view-snap-react?
Unlike many JavaScript-based scroll snap plugins that attempt to recreate or unify scrolling behavior, full-view-snap-react is an optimized, cross-platform tested wrapper that leverages native CSS scroll snapping. This approach allows modern browsers (especially through touch gestures) to deliver the smoothest and most performant scrolling experience possible.
This react library aims to take the burdon away from the developr and checks for browser suppport / features to help deliver the best native scroll snapping experience possible.
Many other plugins restrict users to navigating one section at a time, which can feel cumbersome and unnatural. Some try to recreate momentum scrolling but struggle to get snap points right. Native scroll snapping, as used in this package, excels at handling momentum and snap points across all browsers, letting users scroll naturally and efficiently.
How it works?
The full-view-snap-react component intelligently adapts its rendering strategy based on device capabilities and browser support. Here's how it decides whether to wrap content in a div or render views at the root level:
CSS Unit Support Detection:
- `Measures 100lvh (large viewport height) vs 100svh (small viewport height)
- `If these values differ, the device correctly supports modern CSS viewport units
Touch Device Detection:
- Uses window.matchMedia("(pointer:coarse)") to detect touch-capable devices
- If there's no touch devices, we assume desktop and render on root level for best performance
Live Demo
Interactive demos and documentation are available on the official website.
Examples
- Interactive demos — Basic, Animated, Navigation, Sticky overlays, Dynamic slide count, and Enable Toggle (toggles the
enabledprop at runtime) examples/vite/srcandexamples/next/src— Vite and Next.js source
Installation
To install the package, run:
npm install full-view-snap-reactBrowsers support
| Safari | Edge | Samsung | Firefox | Chrome | iOS Safari | | --------- | --------- | --------- | --------- | --------- | --------- | | 16 (Ventura) | Edge 85+| Android 9+ | 110+ | 85+ | 14+ | | Chrome iOS | Firefox iOS | Chrome Android | Firefox Android | | | | 132+ (IOS 16+) | 141+ (IOS 16+) | 128+ (Android 10+) | v140+ (Android 9+) | | |
Usage
FullViewSnap
FullViewSnap is the primary component for vertical scroll snapping with navigation.
Props
children(optional): React nodes to be rendered as snap sections.hideScrollBars(optional): A boolean that determines whether to hide scroll barstrue: Scroll bars will be hiddenfalse(default): Scroll bars will be visible
enabled(optional): A boolean that toggles full-view snap layout at runtime (default:true)true(default): Slides use viewport height and scroll-snap classes; scroll position is trackedfalse: Viewport height, scroll-snap, and related layout classes are removed fromFullView,EdgeSpacer, and the root scroller. Slides wrap to their content height and the page scrolls like a normal document.Controllercontinues to update scroll state from document scroll;suspendScrollSnap/instateScrollSnapare no-ops until snap is turned back on
render: A function that receives the current view state and returns React nodes to be rendered as snap sections.currentView: The current view index (0-based)totalViews: The total number of viewsscrollPercentage: The current scroll percentage including overscroll spacingcontentScrollPercentage: The content scroll percentagerootScrollerContext: Context object with scroll control methods
Example
import React from 'react';
import ReactDOM from 'react-dom/client';
import { FullViewSnap, Controller, FullView } from 'full-view-snap-react';
const App = () => (
<FullViewSnap
hideScrollBars={true} // Optional: hide scroll bars
render={(
currentView,
totalViews,
scrollPercentage,
contentScrollPercentage
) => (
<>
<Controller>
<FullView>
<h1>view 1</h1>
</FullView>
<FullView>
<h1>view 2</h1>
</FullView>
<FullView>
<h1>view 3</h1>
</FullView>
</Controller>
</>
)}
/>
);
const root = ReactDOM.createRoot(document.body);
root.render(<App />);Disabling snap at runtime
Pass enabled from component state (or any other source) to switch between full-view snap and normal document flow:
import React, { useState } from 'react';
import { FullViewSnap, Controller, FullView } from 'full-view-snap-react';
const App = () => {
const [enabled, setEnabled] = useState(true);
return (
<FullViewSnap
enabled={enabled}
hideScrollBars={true}
render={() => (
<>
<Controller>
<FullView>
<h1>view 1</h1>
<button type="button" onClick={() => setEnabled((v) => !v)}>
Snap {enabled ? 'on' : 'off'}
</button>
</FullView>
<FullView>
<h1>view 2</h1>
</FullView>
</Controller>
</>
)}
/>
);
};See the Enable Toggle demo on the official website, or examples/vite/src/EnableToggle.tsx for the source.
AbsoluteView
AbsoluteView is a simple wrapper component that creates an absolutely positioned container. It's designed to place decorative elements between and over multiple FullView components while preserving native snap behavior.
Props
AbsoluteView extends all standard HTML div attributes (React.HTMLAttributes<HTMLDivElement>) and accepts:
children(optional): React nodes to be rendered inside the absolute containerstyle(optional): CSS styles to apply to the container- All standard div props (className, id, onClick, etc.)
Key Features
- Simple absolute positioning: Creates a basic
position: absolutecontainer - Full div compatibility: Accepts all standard div props and attributes
- No built-in styling: Provides a clean slate for custom styling
- Preserves snap behavior: Doesn't interfere with
FullViewnative scroll snapping
Example
import React from 'react';
import { FullViewSnap, Controller, FullView, AbsoluteView } from 'full-view-snap-react';
const App = () => (
<FullViewSnap
hideScrollBars={true}
render={() => (
<>
<Controller>
<FullView>
<h1>First View</h1>
</FullView>
{/* AbsoluteView for decorative elements */}
<AbsoluteView
style={{
zIndex: 10,
right: "20px",
top: "50%",
transform: "translateY(-50%)"
}}
>
<div style={{
background: "rgba(0,0,0,0.8)",
color: "white",
padding: "1rem",
borderRadius: "8px",
width: "200px",
position: "sticky,
top: "0px"
}}>
🎯 Sticky Element
</div>
</AbsoluteView>
<FullView>
<h1>Second View</h1>
</FullView>
<FullView>
<h1>Third View</h1>
</FullView>
</Controller>
</>
)}
/>
);Use Cases
- Overlays: Position elements that need to persist above or underneath multiple views
- Decorative elements: Add visual elements that stick above the views at specific points
Mobile Considerations
For mobile devices, consider using responsive positioning:
<AbsoluteView
style={{
zIndex: 10,
right: "20px", // Desktop: right side
bottom: "20px", // Mobile: bottom (override with media queries)
width: "300px"
}}
>
{/* Content */}
</AbsoluteView>FullViewSnapContext
The FullViewSnapContext provides access to the current scroll state.
Context Value
interface FullViewSnapContextProps {
contextState: {
currentIndex: number; // Current view index (0-based)
currentScrollPercentage: number; // Current scroll percentage
totalViews: number; // Total number of views
currentContentScrollPercentage: number; // Content scroll percentage
rootScrollerContext: RootScrollerContextProps; // Access to scroll control methods
edgeSpacerRef?: React.RefObject<HTMLDivElement | null>; // Edge spacer reference
};
updateContextState: (newState: contextStateProps) => void; // Function to update context state
}Usage Example
import { FullViewSnapContext } from 'full-view-snap-react';
import { useContext } from 'react';
function MyComponent() {
const { contextState, updateContextState } = useContext(FullViewSnapContext);
console.log('Current view:', contextState.currentIndex);
console.log('Total views:', contextState.totalViews);
console.log('Scroll percentage:', contextState.currentScrollPercentage);
return (
<div>
<p>Current View: {contextState.currentIndex + 1} of {contextState.totalViews}</p>
</div>
);
}RootScrollerContext
The RootScrollerContext provides low-level access to scroll control methods and references.
Context Props
rootScrollerRef: Reference to the scroll container- Type:
MutableRefObject<HTMLDivElement | HTMLElement> | null - Description: Direct reference to the scroll container element
- Type:
enabled: Whether full-view snap layout is active- Type:
boolean - Description: Mirrors the
enabledprop onFullViewSnap. Whenfalse, snap-related behaviour in context helpers is inactive
- Type:
isFixedViewport: Whether using fixed viewport mode- Type:
boolean | null - Description: Indicates if the component is using a wrapper div or rendering at root level
- Type:
scrollToView(index, speed): Scroll to a specific viewindex: View index to scroll to (0-based)speed: Scroll speed in milliseconds (-1 = instant, >0 = smooth scroll)
slideRefs: Array of slide references- Type:
React.MutableRefObject<HTMLDivElement>[] - Description: Array containing references to all slide elements
- Type:
setSlideRefs: Set slide references- Type:
React.Dispatch<React.SetStateAction<React.RefObject<HTMLDivElement | null>[]>> - Description: Function to update the array of slide references
- Type:
suspendScrollSnap(duration): Temporarily disable scroll snappingduration: Duration in milliseconds to suspend snapping (default: 1400ms)
instateScrollSnap(): Re-enable scroll snapping immediately
Usage Example
import { RootScrollerContext } from 'full-view-snap-react';
import { useContext } from 'react';
function NavigationComponent() {
const { scrollToView, suspendScrollSnap, instateScrollSnap } = useContext(RootScrollerContext);
const handleJumpToView = (index) => {
// Suspend scroll snapping temporarily
suspendScrollSnap(1000);
// Scroll to specific view
scrollToView(index, 0); // -1 = instant jump, other values = smooth scroll
// Re-enable scroll snapping after 1 second
setTimeout(() => {
instateScrollSnap();
}, 1000);
};
return (
<div>
<button onClick={() => handleJumpToView(0)}>Go to First View</button>
<button onClick={() => handleJumpToView(2)}>Go to Third View</button>
</div>
);
}Limitation:
This component library is intended for use on the root viewport and requires the ReactDOM root to bedocument.body.Do not wrap
<Controller>or individual<FullView>slides in extra HTML elements when snap is active.
License
This project is licensed under the MIT License.
