Struggling to implement a responsive, performant staggered grid (Masonry layout) in React? I’ve been there, scouring libraries for a solution that supports dynamic content, drag-and-drop, and seamless responsiveness. Most options fell short—until So I made Sentereige. This React component library simplifies creating interactive layouts like staggered grids, lists, and Kanban boards. In this tutorial, I’ll guide you through using Sentereige in a Next.js application to build a Kanban board with a staggered grid layout, complete with drag-and-drop functionality and toast notifications.
Why Sentereige?
Sentereige is a versatile, open-source library that tackles complex layout challenges. Its standout features include:
- Staggered Grid (Masonry Layout): Automatically arranges items left-to-right in the shortest column, handling dynamic sizes.
-
Drag-and-Drop Sorting: Reorder items within a grid/list or across multiple grids with shared
groupIds. - Responsive Design: Adapts to any screen size for a polished user experience.
- Virtual Scrolling: Optimizes performance for large datasets.
- Customizable Drag Handles: Define specific drag areas for intuitive interactions.
- Nested Structures: Supports complex layouts with nested grids and lists.
Below are placeholder demo GIFs showcasing Sentereige’s capabilities (replace with actual GIFs from the Sentereige GitHub repository):
These demos highlight Sentereige’s flexibility. Check the GitHub repo for real examples and detailed documentation.
Tutorial: Building a Kanban Board with Staggered Grid in Next.js
Let’s build a Kanban board with draggable columns (in grid mode) and cards (in list mode) using Sentereige in a Next.js app. The example includes a toast notification system to display interactions like item clicks and drags. Below, I’ll break down the code and explain each step.
Prerequisites
- Node.js and npm/yarn installed.
- A Next.js project (create one with
npx create-next-app@latest). - Basic knowledge of React and Next.js.
Step 1: Install Sentereige
Install Sentereige in your Next.js project:
npm install sentereige
Step 2: Set Up the Toast Notification Utility
To provide feedback when users interact with the Kanban board (e.g., dragging or clicking items), we’ll create a showToast function. This function dynamically creates a styled toast notification that slides in and fades out after 3 seconds.
Create a file at src/utils/toast.ts:
"use client";
export const showToast = (content: string): void => {
// Remove any existing toast
const existingToast = document.querySelector(".toast");
if (existingToast) {
existingToast.remove();
}
const toast = document.createElement("div");
toast.className = "toast";
toast.style.position = "fixed";
toast.style.top = "10px";
toast.style.right = "10px";
toast.style.backgroundColor = "#1f2937";
toast.style.color = "#ffffff";
toast.style.paddingLeft = "1rem";
toast.style.paddingRight = "1rem";
toast.style.paddingTop = "0.5rem";
toast.style.paddingBottom = "0.5rem";
toast.style.borderRadius = "0.5rem";
toast.style.boxShadow =
"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)";
toast.style.display = "flex";
toast.style.alignItems = "center";
toast.style.gap = "0.5rem";
toast.style.maxWidth = "20rem";
toast.style.zIndex = "50";
toast.style.animation = "slide-in 0.3s ease-out";
toast.textContent = content;
// Add CSS animation keyframes
const style = document.createElement("style");
style.textContent = `
@keyframes slide-in {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
document.head.appendChild(style);
document.body.appendChild(toast);
// Auto-remove after 3 seconds with fade-out
setTimeout(() => {
toast.style.transition = "opacity 0.3s ease-out";
toast.style.opacity = "0";
setTimeout(() => toast.remove(), 300);
}, 3000);
};
This function uses Tailwind-inspired CSS for styling and adds a slide-in animation. It’s marked with "use client" since it manipulates the DOM, which is necessary for Next.js client-side components.
Step 3: Create the Kanban Board Component
Now, let’s implement the main component in your Next.js page (e.g., src/app/page.tsx). This component creates a Kanban board with three columns (“To Do,” “In Progress,” “Done”), each containing draggable cards. The columns are arranged in a staggered grid using Sentereige’s grid mode, while the cards within each column use list mode.
Replace the contents of src/app/page.tsx with the following:
"use client";
import Image from "next/image";
import { Sentereige } from "sentereige";
import { showToast } from "../utils/toast";
export default function Home() {
// Define initial data for Kanban columns
const initialColumns = [
{
id: "col1",
title: "To Do",
cards: Array.from({ length: 10 }, (_, i) => ({
id: `todo-${i + 1}`,
content: `Task ${i + 1}`,
color: `hsl(${Math.floor(Math.random() * 360)}, 65%, 60%)`,
})),
},
{
id: "col2",
title: "In Progress",
cards: Array.from({ length: 5 }, (_, i) => ({
id: `progress-${i + 1}`,
content: `Task ${i + 1}`,
color: `hsl(${Math.floor(Math.random() * 360)}, 70%, 65%)`,
})),
},
{
id: "col3",
title: "Done",
cards: Array.from({ length: 3 }, (_, i) => ({
id: `done-${i + 1}`,
content: `Task ${i + 1}`,
color: `hsl(${Math.floor(Math.random() * 360)}, 70%, 65%)`,
})),
},
];
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
{/* Next.js logo */}
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
{/* Outer Sentereige for column grid */}
<Sentereige
mode="grid"
dragHandleSelector=".drag-handle"
isSortable
options={{ gutter: 16, scrollSpeed: 10, scrollThreshold: 100 }}
style={{ minHeight: "60vh", width: "1200px" }}
>
{initialColumns.map((column) => (
<div
key={column.id}
style={{
width: "300px",
}}
>
{/* Column header with drag handle */}
<h2
className="drag-handle"
style={{
border: "8px solid rgba(0, 0, 0, 0.1)",
borderRadius: "8px",
background: "#4a90e2",
color: "#ffffff",
fontSize: "1.1rem",
fontWeight: "500",
padding: "12px",
margin: 0,
textAlign: "center",
borderBottom: "1px solid #d0d0d0",
cursor: "grab",
}}
>
{column.title}
</h2>
{/* Inner Sentereige for card list */}
<Sentereige
id={column.id}
groupId="g"
mode="list"
isSortable
onMovedEvent={(
key,
fromGroupId,
fromPosition,
toGroupId,
toPosition
) => {
showToast(
`Item moved: ${key} from group ${fromGroupId} index ${fromPosition} to group ${toGroupId} index ${toPosition}`
);
}}
onItemClick={(key: string) => showToast(`Item clicked! ${key}`)}
>
{column.cards.map((card) => (
<div
key={card.id}
style={{
border: "8px solid rgba(0, 0, 0, 0.1)",
borderRadius: "8px",
cursor: "pointer",
gap: "6px",
padding: "16px",
background: card.color,
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
overflow: "hidden",
transition: "background 0.2s ease",
margin: "4px",
userSelect: "none",
}}
>
<h1
style={{
margin: 0,
fontSize: "1rem",
fontWeight: "500",
color: "#ffffff",
textAlign: "center",
lineHeight: "1.3",
}}
>
{card.id}
</h1>
</div>
))}
</Sentereige>
</div>
))}
</Sentereige>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org →
</a>
</footer>
</div>
);
}
Step 4: Understanding the Code
Let’s break down the key parts of the page.tsx code:
-
Data Structure:
- The
initialColumnsarray defines three columns: “To Do” (10 cards), “In Progress” (5 cards), and “Done” (3 cards). - Each card has an
id,content, and a randomcolorfor visual distinction.
- The
-
Outer Sentereige (Grid Mode):
- The outer
<Sentereige>component is set tomode="grid"to create a staggered grid of columns. -
dragHandleSelector=".drag-handle"ensures only the column headers (with classdrag-handle) are draggable. -
isSortableenables drag-and-drop for columns. -
options={{ gutter: 16, scrollSpeed: 10, scrollThreshold: 100 }}configures the grid’s spacing and scrolling behavior. -
style={{ minHeight: "60vh", width: "1200px" }}sets the grid’s dimensions.
- The outer
-
Inner Sentereige (List Mode):
- Each column contains an inner
<Sentereige>component set tomode="list"for vertical card arrangement. -
id={column.id}andgroupId="g"allow cards to be dragged across columns within the same group. -
onMovedEventtriggers a toast notification when a card is moved, showing details like the item’s key and position. -
onItemClicktriggers a toast when a card is clicked.
- Each column contains an inner
-
Styling:
- Columns and cards use inline styles for borders, padding, and colors, with Tailwind-inspired classes for the layout.
- The
drag-handleclass on the column header (<h2>) makes it draggable, with acursor: grabfor user feedback.
Step 5: Run the Application
- Run your Next.js app:
npm run dev
- Open
http://localhost:3000in your browser. You’ll see a Kanban board with three columns, where you can:- Drag columns to reorder them (via the header).
- Drag cards within a column or across columns.
- Click cards to see toast notifications.
Step 6: Customize and Extend
-
Add Real Data: Replace
initialColumnswith data from an API or state management (e.g., Redux, Zustand). - Enhance Styling: Use Tailwind CSS or a CSS-in-JS library like Emotion for more complex styles.
- Add Features: Implement card creation/deletion or persist the board state to a backend.
Conclusion
Sentereige solved my struggles with staggered grids in React, offering a flexible, performant solution for layouts like this Kanban board. Its drag-and-drop, responsive design, and nested structure support make it a go-to for complex UIs. Try it out in your Next.js project, and explore the Sentereige GitHub repo for more examples and documentation.
If you’ve been wrestling with layout challenges, Sentereige could be your game-changer. Let me know in the comments how you’re using it or if you have questions!
Tags: react, nextjs, ui, layout, opensource




Top comments (0)