A shadcn/ui compatible polymorphic React component library for creating resizable, responsive grid layouts that adapt to any context.
⚠️ Alpha Release (v0.1.0) - This is a pre-1.0 release with limited functionality. Currently only Grid mode (desktop) is fully implemented and tested. Responsive modes (Dock, Tabs, etc.) are in development. API may change before 1.0. Use at your own risk in production.
🎯 Polymorphic Design - Handle multiple tasks or contexts within a single view 📱 Responsive Modes - Automatically switch between grid, dock, tabs, and other layouts 🎛️ Resizable Components - CSS Grid-powered with fractional and pixel units 💾 State Persistence - localStorage, sessionStorage, or custom persistence ⌨️ Keyboard Navigation - Full accessibility support with keyboard controls 🎨 Shadcn/UI Compatible - Works seamlessly with modern design systems 🔧 TypeScript First - Comprehensive type safety and IntelliSense
PrettyPoly offers two installation methods:
Like shadcn/ui, copy components directly into your project for full control and proper Tailwind class generation:
# Initialize shadcn (if you haven't already)
npx shadcn@latest init
# Add PrettyPoly components
npx shadcn@latest add https://pretty-poly.github.io/react/r/grid-systemWhy copy? When you import from npm, Tailwind can't scan the component files in node_modules, so CSS classes won't be generated. Copying ensures:
- ✅ All Tailwind classes are generated
- ✅ Full control to customize components
- ✅ No version conflicts
- ✅ TypeScript types included
See docs/INSTALLATION.md for detailed installation instructions.
For quick prototyping or if you prefer traditional npm packages:
# New projects
npm install @pretty-poly/react tailwindcss
# Existing shadcn/ui projects
npm install @pretty-poly/reactNote: With npm installation, you may need to configure Tailwind's safelist to ensure all component classes are included.
The library is fully compatible with existing shadcn/ui setups and will inherit your theme configuration automatically.
import { Grid, Block, Divider, cn } from '@pretty-poly/react'
import '@pretty-poly/react/styles'
const layout = [
{
id: 'root',
type: 'group',
direction: 'row',
order: 0
},
{
id: 'sidebar',
type: 'block',
defaultSize: 300,
minSize: 200,
maxSize: 500,
sizeUnit: 'px',
dividerPosition: 'end',
parentId: 'root',
order: 0
},
{
id: 'main',
type: 'block',
defaultSize: 1,
sizeUnit: 'fr',
parentId: 'root',
order: 1
}
]
function App() {
return (
<Grid defaultLayout={layout} persist="localStorage">
<Block id="sidebar" className="bg-gray-100 p-4">
<h2>Sidebar</h2>
<nav>...</nav>
</Block>
<Divider targetId="sidebar" position="end" />
<Block id="main" className="p-6">
<h1>Main Content</h1>
<p>Resizable content area</p>
</Block>
</Grid>
)
}PrettyPoly automatically adapts to different screen sizes and interaction patterns:
const responsiveModes = {
mobile: { type: 'dock', maxWidth: 767 },
tablet: { type: 'tabs', minWidth: 768, maxWidth: 1023 },
desktop: { type: 'grid', minWidth: 1024 }
}
<Grid modes={responsiveModes}>
<Block
id="sidebar"
desktop={{ defaultSize: 300, collapsible: true }}
mobile={{ icon: MenuIcon, label: "Menu", dockOrder: 1 }}
tablet={{ tabLabel: "Navigation" }}
>
Content adapts automatically
</Block>
</Grid>Root container that manages layout state and responsive behavior.
<Grid
defaultLayout={blocks}
modes={responsiveModes}
persist="localStorage"
onLayoutChange={handleChange}
onModeChange={handleModeChange}
/>Content containers that can be resized, collapsed, and configured per mode.
<Block
id="unique-id"
type="block" // or "group"
desktop={{ defaultSize: 300, minSize: 200 }}
mobile={{ icon: Icon, label: "Label" }}
>
Content
</Block>Draggable handles for resizing between blocks.
<Divider
targetId="block-to-resize"
position="end" // or "start"
size={8}
className="hover:bg-blue-200"
/>Full resizable grid with draggable dividers, collapse/expand, and keyboard navigation.
Bottom navigation with icon-based panel switching, similar to mobile apps.
Traditional tabbed interface for medium-sized screens.
Full-screen panels with swipe navigation for mobile interfaces.
Collapsible sidebar with main content area.
Expandable vertical sections for content hierarchy.
Save and restore layout state across sessions:
// localStorage
<Grid persist="localStorage" persistKey="my-layout" />
// sessionStorage
<Grid persist="sessionStorage" />
// Custom function
<Grid persist={(state) => saveToDatabase(state)} />- Arrow Keys: Navigate between blocks
- Ctrl + Arrow Keys: Resize focused block
- Ctrl + Plus/Minus: Expand/collapse block
- Enter/Space: Toggle collapse state
- Escape: Remove focus
Full TypeScript support with comprehensive type definitions:
import type {
BlockConfig,
GridProps,
ResponsiveModes,
LayoutMode
} from '@pretty-poly/react':root {
--pretty-poly-border: hsl(214.3 31.8% 91.4%);
--pretty-poly-foreground: hsl(222.2 84% 4.9%);
--pretty-poly-background: hsl(0 0% 100%);
/* ... */
}<Divider
handle={({ className }) => (
<div className={`custom-handle ${className}`} />
)}
/>See the /examples directory for:
- Basic resizable layout
- Comprehensive responsive demo
- VS Code-style interface
- Shadcn/UI integration
- Custom mode examples
| Prop | Type | Description |
|---|---|---|
defaultLayout |
BlockConfig[] |
Initial layout configuration |
modes |
ResponsiveModes |
Responsive mode definitions |
persist |
boolean | string | function |
State persistence options |
onLayoutChange |
function |
Layout change callback |
onModeChange |
function |
Mode change callback |
| Prop | Type | Description |
|---|---|---|
id |
string |
Unique block identifier |
type |
'block' | 'group' |
Block type |
[mode] |
ModeConfig |
Mode-specific configuration |
useGridResize()- Handle resize operationsuseGridMode()- Manage responsive modesuseGridPersistence()- State persistenceuseGridKeyboard()- Keyboard navigation
Pretty Poly automatically inherits your shadcn/ui theme:
// Uses your shadcn theme colors automatically
<Block className="bg-card border border-border text-card-foreground">
Content styled with your theme
</Block>Supports shadcn/ui dark mode out of the box:
// Add dark mode toggle to your app
<div className="dark"> {/* or use next-themes */}
<Grid>
{/* Components automatically adapt to dark theme */}
</Grid>
</div>Use the cn utility for conditional classes:
import { cn } from '@pretty-poly/react'
<Block
className={cn(
"bg-background",
isActive && "border-primary",
isCollapsed && "opacity-50"
)}
>Pretty Poly uses a hybrid approach:
- Core functionality: Custom CSS for dynamic grid layouts (required)
- Visual styling: Tailwind CSS + shadcn/ui variables (customizable)
This ensures the dynamic resizing works perfectly while maintaining full shadcn/ui compatibility.
This library was extracted from the grid_panes Phoenix LiveView implementation and modernized for React. See the original implementation at /path/to/grid_panes.
MIT © Amazing Team
PrettyPoly - Where layouts adapt to every context 🎭