React Sticky Node is a performant library for creating sticky (fixed-position) elements in React applications. It provides a simple way to make elements stick to the top of the viewport when scrolling, with support for boundaries, offsets, and smooth transitions. This guide walks through setting up and creating sticky elements using React Sticky Node with React, from installation to a working implementation. This is part 47 of a series on using React Sticky Node with React.
Prerequisites
Before you begin, make sure you have:
- Node.js version 14.0 or higher installed
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) or create-react-app setup
- Basic knowledge of React hooks (useState, useEffect)
- Familiarity with JavaScript/TypeScript
- Understanding of CSS positioning
Installation
Install React Sticky Node using your preferred package manager:
npm install react-stickynode
Or with yarn:
yarn add react-stickynode
Or with pnpm:
pnpm add react-stickynode
After installation, your package.json should include:
{
"dependencies": {
"react-stickynode": "^3.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
React Sticky Node requires minimal setup. Import the component and you're ready to use it.
First Example / Basic Usage
Let's create a simple sticky header. Create a new file src/StickyExample.jsx:
// src/StickyExample.jsx
import React from 'react';
import Sticky from 'react-stickynode';
function StickyExample() {
return (
<div>
<Sticky top={0}>
<header style={{
backgroundColor: '#2c3e50',
color: 'white',
padding: '15px 30px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
zIndex: 1000
}}>
<h1 style={{ margin: 0, fontSize: '24px' }}>Sticky Header</h1>
</header>
</Sticky>
<div style={{ padding: '40px', minHeight: '200vh' }}>
<h2>Scroll down to see the header stick to the top</h2>
<p>The header will stick to the top when you scroll past it.</p>
<div style={{ height: '1500px', backgroundColor: '#f0f0f0', padding: '20px' }}>
<p>Long content area to demonstrate sticky behavior...</p>
</div>
</div>
</div>
);
}
export default StickyExample;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import StickyExample from './StickyExample';
import './App.css';
function App() {
return (
<div className="App">
<StickyExample />
</div>
);
}
export default App;
This creates a header that sticks to the top of the viewport when you scroll past it.
Understanding the Basics
React Sticky Node provides several key features:
- Sticky component: Wraps elements to make them sticky
- top prop: Sets the offset from the top when sticky
- bottomBoundary: Sets a boundary where sticky behavior stops
- innerZ: Z-index for the sticky element
- enabled: Enable/disable sticky behavior
- State callbacks: Callbacks for sticky state changes
Key concepts:
- Sticky Behavior: Element becomes fixed when scrolling past its original position
- Top Offset: Distance from top of viewport when sticky
- Boundaries: Limits where sticky behavior applies
- State Management: Track when element becomes sticky or unsticky
- Performance: Optimized for smooth scrolling performance
Here's an example with navigation and boundaries:
// src/StickyNavigation.jsx
import React, { useState } from 'react';
import Sticky from 'react-stickynode';
function StickyNavigation() {
const [status, setStatus] = useState('');
const handleStateChange = (status) => {
setStatus(status.status);
};
const navItems = ['Home', 'About', 'Services', 'Contact'];
return (
<div>
<Sticky
top={0}
bottomBoundary={1200}
innerZ={1000}
onStateChange={handleStateChange}
>
<nav style={{
backgroundColor: '#007bff',
color: 'white',
padding: '15px 30px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
<div style={{ fontSize: '20px', fontWeight: 'bold' }}>
Logo
</div>
<div style={{ display: 'flex', gap: '20px' }}>
{navItems.map(item => (
<a
key={item}
href={`#${item.toLowerCase()}`}
style={{
color: 'white',
textDecoration: 'none',
padding: '5px 10px',
borderRadius: '4px',
transition: 'background-color 0.3s'
}}
onMouseEnter={(e) => e.target.style.backgroundColor = 'rgba(255,255,255,0.2)'}
onMouseLeave={(e) => e.target.style.backgroundColor = 'transparent'}
>
{item}
</a>
))}
</div>
</nav>
</Sticky>
<div style={{ padding: '40px', minHeight: '200vh' }}>
<p>Status: {status}</p>
<div style={{ height: '1500px', backgroundColor: '#f8f9fa', padding: '20px' }}>
<p>Scroll to see the navigation stick to the top.</p>
<p>It will stop being sticky after scrolling past 1200px.</p>
</div>
</div>
</div>
);
}
export default StickyNavigation;
Practical Example / Building Something Real
Let's build a complete layout with sticky sidebar and navigation:
// src/LayoutWithSticky.jsx
import React, { useState } from 'react';
import Sticky from 'react-stickynode';
function LayoutWithSticky() {
const [navStatus, setNavStatus] = useState('');
const menuItems = [
{ id: 1, label: 'Home', href: '#home' },
{ id: 2, label: 'About', href: '#about' },
{ id: 3, label: 'Services', href: '#services' },
{ id: 4, label: 'Portfolio', href: '#portfolio' },
{ id: 5, label: 'Contact', href: '#contact' }
];
return (
<div>
<Sticky
top={0}
innerZ={1000}
onStateChange={(status) => setNavStatus(status.status)}
>
<header style={{
backgroundColor: '#ffffff',
boxShadow: navStatus === Sticky.STATUS_FIXED
? '0 2px 8px rgba(0,0,0,0.15)'
: 'none',
padding: '15px 30px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
transition: 'box-shadow 0.3s'
}}>
<div style={{
fontSize: '24px',
fontWeight: 'bold',
color: '#2c3e50'
}}>
MyApp
</div>
<nav style={{ display: 'flex', gap: '30px' }}>
{menuItems.map(item => (
<a
key={item.id}
href={item.href}
style={{
color: '#2c3e50',
textDecoration: 'none',
padding: '8px 15px',
borderRadius: '4px',
transition: 'all 0.3s',
fontWeight: '500'
}}
onMouseEnter={(e) => {
e.target.style.backgroundColor = '#f8f9fa';
e.target.style.color = '#007bff';
}}
onMouseLeave={(e) => {
e.target.style.backgroundColor = 'transparent';
e.target.style.color = '#2c3e50';
}}
>
{item.label}
</a>
))}
</nav>
</header>
</Sticky>
<div style={{ display: 'flex', minHeight: '100vh' }}>
<aside style={{ width: '250px', backgroundColor: '#f8f9fa', padding: '20px' }}>
<Sticky top={80} bottomBoundary={'.main-content'}>
<div style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
<h3 style={{ marginTop: 0 }}>Sidebar</h3>
<ul style={{ listStyle: 'none', padding: 0 }}>
<li style={{ marginBottom: '10px' }}>
<a href="#section1" style={{ color: '#007bff', textDecoration: 'none' }}>
Section 1
</a>
</li>
<li style={{ marginBottom: '10px' }}>
<a href="#section2" style={{ color: '#007bff', textDecoration: 'none' }}>
Section 2
</a>
</li>
<li style={{ marginBottom: '10px' }}>
<a href="#section3" style={{ color: '#007bff', textDecoration: 'none' }}>
Section 3
</a>
</li>
</ul>
</div>
</Sticky>
</aside>
<main className="main-content" style={{ flex: 1, padding: '40px' }}>
<section id="section1" style={{ marginBottom: '60px', minHeight: '800px' }}>
<h2>Section 1</h2>
<p>This is section 1 content. The sidebar will stick when scrolling.</p>
</section>
<section id="section2" style={{ marginBottom: '60px', minHeight: '800px' }}>
<h2>Section 2</h2>
<p>This is section 2 content.</p>
</section>
<section id="section3" style={{ marginBottom: '60px', minHeight: '800px' }}>
<h2>Section 3</h2>
<p>This is section 3 content.</p>
</section>
</main>
</div>
</div>
);
}
export default LayoutWithSticky;
Now create a sticky table header:
// src/StickyTableHeader.jsx
import React from 'react';
import Sticky from 'react-stickynode';
function StickyTableHeader() {
const tableData = Array.from({ length: 50 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
category: ['Electronics', 'Clothing', 'Books', 'Home'][i % 4],
price: (Math.random() * 1000).toFixed(2)
}));
return (
<div style={{ padding: '40px' }}>
<h2>Table with Sticky Header</h2>
<Sticky top={0} innerZ={100}>
<table style={{
width: '100%',
borderCollapse: 'collapse',
backgroundColor: 'white',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
<thead>
<tr style={{ backgroundColor: '#007bff', color: 'white' }}>
<th style={{ padding: '12px', textAlign: 'left', border: '1px solid #ddd' }}>
ID
</th>
<th style={{ padding: '12px', textAlign: 'left', border: '1px solid #ddd' }}>
Name
</th>
<th style={{ padding: '12px', textAlign: 'left', border: '1px solid #ddd' }}>
Category
</th>
<th style={{ padding: '12px', textAlign: 'left', border: '1px solid #ddd' }}>
Price
</th>
</tr>
</thead>
</table>
</Sticky>
<table style={{
width: '100%',
borderCollapse: 'collapse',
marginTop: 0
}}>
<tbody>
{tableData.map(item => (
<tr key={item.id} style={{ borderBottom: '1px solid #ddd' }}>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>{item.id}</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>{item.name}</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>{item.category}</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>${item.price}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default StickyTableHeader;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import LayoutWithSticky from './LayoutWithSticky';
import StickyTableHeader from './StickyTableHeader';
import './App.css';
function App() {
return (
<div className="App">
<LayoutWithSticky />
<StickyTableHeader />
</div>
);
}
export default App;
This example demonstrates:
- Sticky navigation header
- Sticky sidebar with boundaries
- Sticky table header
- State change callbacks
- Custom styling based on sticky state
- Multiple sticky elements
Common Issues / Troubleshooting
Element not sticking: Make sure there's enough content to scroll. If the page content is shorter than the viewport, the element won't become sticky. Also check that the
topprop is set correctly.Z-index issues: Use the
innerZprop to set the z-index of the sticky element. This ensures it appears above other content when sticky.Sticky not releasing: Set the
bottomBoundaryprop to define where the sticky behavior should stop. This can be a pixel value or a CSS selector.Performance issues: React Sticky Node is optimized for performance, but if you have many sticky elements, consider using
enabledprop to conditionally enable/disable sticky behavior.State not updating: Use the
onStateChangecallback to track sticky state changes. The status can beSTATUS_ORIGINAL,STATUS_RELEASED, orSTATUS_FIXED.Boundary not working: Make sure the boundary selector or value is correct. For CSS selectors, ensure the element exists in the DOM when the Sticky component mounts.
Next Steps
Now that you have an understanding of React Sticky Node:
- Explore advanced boundary configurations
- Learn about state management and callbacks
- Implement conditional sticky behavior
- Add animations and transitions
- Integrate with React Router for navigation
- Learn about other sticky libraries
- Check the official repository: https://github.com/yahoo/react-stickynode
- Look for part 48 of this series for more advanced topics
Summary
You've successfully set up React Sticky Node in your React application and created sticky navigation headers, sidebars, and table headers. React Sticky Node provides a performant solution for creating elements that stick to the viewport when scrolling.
SEO Keywords
react-stickynode
React sticky element
react-stickynode tutorial
React fixed position
react-stickynode installation
React sticky header
react-stickynode example
React sticky navigation
react-stickynode setup
React sticky sidebar
react-stickynode customization
React scroll sticky
react-stickynode boundaries
React sticky library
react-stickynode getting started
Top comments (0)