Ever struggled with building complex, interactive grid layouts in React? Does CSS feel too flexible ,making it hard to keep your intent clear? Meet GridLookout—a layout system that brings the structured power of design software layers to React development. With its unique viewport-based approach, GridLookout helps you design layouts to place content where it belongs.
What Makes GridLookout Different?
Most grid systems work with fixed grid sizes and absolute positioning. GridLookout takes a more flexible approach by:
- Using viewport-relative measurements for each layer
- Supporting relative positioning within cells (using values from 0 to 1)
- Enabling named cell addressing for semantic layouts
- Allowing independent layer configurations with their own viewports
- A cell in GridLookout hosts a ReactJS component enclosed in a fragment so that cell content is not influenced by any css properties outside the content.
Core Concepts
Viewport-Based Layer Architecture
Each layer in GridLookout defines its own viewport and contains cells with relative positioning:
const gridSchema = {
layers: [
{
name: "MainLayer",
viewport: { width: 600, height: 800 },
cells: {
header: {
startX: 0, // Starts at 0% of viewport width
startY: 0, // Starts at 0% of viewport height
width: 1, // Takes 100% of viewport width
height: 0.1, // Takes 10% of viewport height
content: "HeaderComponent"
},
sidebar: {
startX: 0, // Starts at 0% of viewport width
startY: 0.1, // Starts at 10% of viewport height
width: 0.2, // Takes 20% of viewport width
height: 0.9, // Takes 90% of viewport height
content: "SidebarComponent"
}
}
}
]
};
Semantic Cell Addressing
Instead of using numeric indices, cells are addressed by meaningful names:
// Traditional grid systems
grid[0][0] // Top-left cell
// GridLookout
layer.cells.header // Header cell
layer.cells.sidebar // Sidebar cell
layer.cells.main // Main content cell
The Advantage of Unique Addresses
GridLookout’s approach to assigning unique addresses to each cell provides significant benefits, including:
Precise State Management:
Each cell's unique address allows developers to manage application state at a granular level. This makes it easier to track, update, and control specific areas of the layout in real time.
Dynamic Content Updates:
With unique addresses, updating a cell's content or properties dynamically becomes straightforward, without affecting other parts of the layout.
Improved Debugging:
Debugging and testing individual cells is simplified, as each cell can be identified and isolated using its unique address.
Enhanced Flexibility:
Developers can implement complex features, such as animations, conditional rendering, or interactivity, tied directly to specific cells without ambiguity.
Interoperability with External Systems:
Unique addresses make it easier to integrate with other systems, such as APIs or backend services, that may need to target specific parts of the UI.
Building a Viewport-Aware GridLookout Component
Here's how to implement the viewport-based system in React:
import React from 'react';
const GridLayer = ({ layer }) => {
const { viewport, cells } = layer;
return (
<div
className="grid-layer"
style={{
position: 'relative',
width: viewport.width,
height: viewport.height
}}
>
{Object.entries(cells).map(([cellName, cell]) => (
<div
key={cellName}
className="grid-cell"
style={{
position: 'absolute',
left: `${cell.startX * 100}%`,
top: `${cell.startY * 100}%`,
width: `${cell.width * 100}%`,
height: `${cell.height * 100}%`,
border: '1px dashed #ccc'
}}
>
{renderContent(cell.content)}
</div>
))}
</div>
);
};
const GridLookout = ({ schema }) => {
return (
<div className="grid-container">
{schema.layers.map((layer) => (
<GridLayer
key={layer.name}
layer={layer}
/>
))}
</div>
);
};
// Component registry for dynamic content rendering
const componentRegistry = {
HeaderComponent: () => <header>Header Content</header>,
SidebarComponent: () => <nav>Sidebar Content</nav>,
MainComponent: () => <main>Main Content</main>
};
const renderContent = (componentName) => {
const Component = componentRegistry[componentName];
return Component ? <Component /> : null;
};
export default GridLookout;
Managing Layer State with Viewport Awareness
Here's how to handle viewport-aware state management:
import { useState, useCallback } from 'react';
const useGridState = (initialSchema) => {
const [schema, setSchema] = useState(initialSchema);
const updateViewport = useCallback((layerName, newViewport) => {
setSchema(prev => ({
...prev,
layers: prev.layers.map(layer =>
layer.name === layerName
? { ...layer, viewport: newViewport }
: layer
)
}));
}, []);
const updateCell = useCallback((layerName, cellName, updates) => {
setSchema(prev => ({
...prev,
layers: prev.layers.map(layer =>
layer.name === layerName
? {
...layer,
cells: {
...layer.cells,
[cellName]: { ...layer.cells[cellName], ...updates }
}
}
: layer
)
}));
}, []);
return { schema, updateViewport, updateCell };
};
Example Usage
Here's how to use GridLookout in a real application:
const App = () => {
const initialSchema = {
layers: [
{
name: "MainLayer",
viewport: { width: 600, height: 800 },
cells: {
header: {
startX: 0,
startY: 0,
width: 1,
height: 0.1,
content: "HeaderComponent"
},
sidebar: {
startX: 0,
startY: 0.1,
width: 0.2,
height: 0.9,
content: "SidebarComponent"
},
main: {
startX: 0.2,
startY: 0.1,
width: 0.8,
height: 0.9,
content: "MainComponent"
}
}
}
]
};
const { schema, updateViewport } = useGridState(initialSchema);
// Handle viewport updates on window resize
useEffect(() => {
const handleResize = () => {
updateViewport("MainLayer", {
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [updateViewport]);
return <GridLookout schema={schema} />;
};
here is the look
]
Have you built something interesting with GridLookout? Share your experience in the comments below!
SOCIAL SHARE CARD GENERATOR