Page Settings System - User Guide
Overview
The page settings system allows you to save and restore user preferences per page, such as:
- Column count in grid views (2, 3, or 4 columns)
- View mode (grid vs table)
- Items per page
- Any other page-specific UI state
Settings are:
- ✅ User-specific - Each user has their own settings
- ✅ Page-specific - Different pages have independent settings
- ✅ Persistent - Saved in localStorage across browser sessions
- ✅ Automatic - No backend changes needed
Quick Start
1. Import the Hook and Store
import { usePageStore } from "@lib/pageStore"
import { useUserAuth } from "@contexts/UserAuthProvider"
import { usePathname } from "next/navigation"
2. Get User ID and Pathname
const { user } = useUserAuth()
const pathname = usePathname() || ""
const { savePageSettings, getPageSettings } = usePageStore()
const userId = user?.id || "guest"
3. Load Settings on Mount
const [columns, setColumns] = useState<2 | 3 | 4>(() => {
// Load from localStorage on initial render
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.columns as 2 | 3 | 4) || 4 // Default to 4
})
4. Save Settings When Changed
const handleColumnsChange = (newColumns: number) => {
setColumns(newColumns as 2 | 3 | 4)
// Save to localStorage
savePageSettings(userId, pathname, {
columns: newColumns as 2 | 3 | 4,
})
}
5. Pass to GridBlock
<GridBlock
columns={columns}
onColumnsChange={handleColumnsChange}
// ... other props
>
Complete Example: Contacts Page
Here's how to add persistent column settings to the contacts page:
"use client"
import { useState, useEffect } from "react"
import { useUserAuth } from "@contexts/UserAuthProvider"
import { usePathname } from "next/navigation"
import { usePageStore } from "@lib/pageStore"
import { GridBlock } from "@ux/PageContainer"
export default function ContactsPage() {
const { user } = useUserAuth()
const pathname = usePathname() || ""
const { savePageSettings, getPageSettings } = usePageStore()
const userId = user?.id || "guest"
// Load settings from localStorage
const [columns, setColumns] = useState<2 | 3 | 4>(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.columns as 2 | 3 | 4) || 4
})
const [viewMode, setViewMode] = useState<"grid" | "table">(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.viewMode as "grid" | "table") || "grid"
})
// Save columns when changed
const handleColumnsChange = (newColumns: number) => {
setColumns(newColumns as 2 | 3 | 4)
savePageSettings(userId, pathname, { columns: newColumns, viewMode })
}
// Save view mode when changed
const handleViewModeChange = (newMode: "grid" | "table") => {
setViewMode(newMode)
savePageSettings(userId, pathname, { columns, viewMode: newMode })
}
return (
<GridBlock
columns={columns}
onColumnsChange={handleColumnsChange}
viewMode={viewMode}
onViewModeChange={handleViewModeChange}
defaultColumns={4}
showColumnSelector={true}
>
{/* Your cards here */}
</GridBlock>
)
}
What Gets Saved?
The PageSettings type supports:
interface PageSettings {
columns?: 2 | 3 | 4
viewMode?: "grid" | "table"
itemsPerPage?: number
[key: string]: any // Any other custom settings
}
You can save any page-specific UI state:
- Column count
- View mode (grid/table)
- Items per page
- Sort order
- Filter states
- Expanded/collapsed sections
- Custom preferences
Storage Key Format
Settings are stored with this key format:
aiqlick-page-store → pageSettings → "{userId}:{pathname}"
Examples:
"user-123:/company/contacts"- User 123's contacts page settings"user-456:/company/jobs"- User 456's jobs page settings"guest:/jobseeker/jobs"- Guest user's job seeker jobs page
This ensures:
- ✅ Each user has separate settings
- ✅ Each page has separate settings
- ✅ No conflicts between users or pages
Advanced Usage
Save Multiple Settings at Once
savePageSettings(userId, pathname, {
columns: 3,
viewMode: "grid",
itemsPerPage: 20,
sortBy: "name",
sortOrder: "asc",
})
Load Settings with Defaults
const settings = getPageSettings(userId, pathname) || {
columns: 4,
viewMode: "grid",
itemsPerPage: 12,
}
Clear Settings
import { usePageStore } from "@lib/pageStore"
const { clearPageSettings } = usePageStore()
clearPageSettings(userId, pathname)
Custom Hook (Recommended)
Create a reusable hook for your page settings:
// lib/lib/hooks/useContactsPageSettings.ts
import { useState, useEffect } from "react"
import { useUserAuth } from "@contexts/UserAuthProvider"
import { usePathname } from "next/navigation"
import { usePageStore } from "@lib/pageStore"
interface ContactsPageSettings {
columns: 2 | 3 | 4
viewMode: "grid" | "table"
itemsPerPage: number
}
export function useContactsPageSettings() {
const { user } = useUserAuth()
const pathname = usePathname() || ""
const { savePageSettings, getPageSettings } = usePageStore()
const userId = user?.id || "guest"
// Load settings
const [settings, setSettings] = useState<ContactsPageSettings>(() => {
const saved = getPageSettings(userId, pathname)
return {
columns: (saved?.columns as 2 | 3 | 4) || 4,
viewMode: (saved?.viewMode as "grid" | "table") || "grid",
itemsPerPage: (saved?.itemsPerPage as number) || 12,
}
})
// Update settings
const updateSettings = (updates: Partial<ContactsPageSettings>) => {
const newSettings = { ...settings, ...updates }
setSettings(newSettings)
savePageSettings(userId, pathname, newSettings)
}
return {
settings,
updateColumns: (columns: 2 | 3 | 4) => updateSettings({ columns }),
updateViewMode: (viewMode: "grid" | "table") => updateSettings({ viewMode }),
updateItemsPerPage: (itemsPerPage: number) => updateSettings({ itemsPerPage }),
}
}
Then use it in your page:
const { settings, updateColumns, updateViewMode } = useContactsPageSettings()
<GridBlock
columns={settings.columns}
onColumnsChange={updateColumns}
viewMode={settings.viewMode}
onViewModeChange={updateViewMode}
/>
Implementation Checklist
When adding persistent settings to a page:
- Import
usePageStore,useUserAuth, andusePathname - Get
userIdfromuser?.id(with fallback to "guest") - Get
pathnamefromusePathname() - Initialize state by loading from
getPageSettings(userId, pathname) - Provide default values for when settings don't exist
- Save settings in onChange handlers using
savePageSettings() - Pass controlled values to
GridBlock(columns, viewMode, etc.) - Test: Change settings → Refresh page → Settings should persist
Common Patterns
Pattern 1: Simple Column Persistence
const [columns, setColumns] = useState<2 | 3 | 4>(() => {
const saved = usePageStore.getState().getPageSettings(userId, pathname)
return (saved?.columns as 2 | 3 | 4) || 4
})
const handleColumnsChange = (cols: number) => {
setColumns(cols as 2 | 3 | 4)
savePageSettings(userId, pathname, { columns: cols })
}
Pattern 2: Multiple Settings
const [settings, setSettings] = useState(() => {
const saved = getPageSettings(userId, pathname) || {}
return {
columns: (saved.columns as 2 | 3 | 4) || 4,
viewMode: (saved.viewMode as "grid" | "table") || "grid",
itemsPerPage: (saved.itemsPerPage as number) || 12,
}
})
const updateSetting = (key: string, value: any) => {
const newSettings = { ...settings, [key]: value }
setSettings(newSettings)
savePageSettings(userId, pathname, newSettings)
}
Pattern 3: useEffect for Complex Logic
useEffect(() => {
// Save whenever settings change
savePageSettings(userId, pathname, {
columns,
viewMode,
itemsPerPage,
sortBy,
sortOrder,
})
}, [columns, viewMode, itemsPerPage, sortBy, sortOrder])
Debugging
Check localStorage
Open browser DevTools → Application → Local Storage → aiqlick-page-store
You should see:
{
"state": {
"pageSettings": {
"user-123:/company/contacts": {
"columns": 3,
"viewMode": "grid"
}
}
},
"version": 0
}
Console Logging
console.log("Loaded settings:", getPageSettings(userId, pathname))
console.log("Saving settings:", { columns, viewMode })
Clear All Settings
// In browser console
localStorage.removeItem('aiqlick-page-store')
FAQs
Q: Do settings work for guest/unauthenticated users?
A: Yes! Use userId = user?.id || "guest" to support guest users.
Q: What happens if two users share a computer? A: Each user has separate settings because they have different user IDs.
Q: Can I save settings per company?
A: Yes! Use companyId instead of userId as the first parameter, or combine them: ${userId}:${companyId}.
Q: Will this work across devices? A: No, localStorage is per-browser. For cross-device sync, you'd need to save to the backend instead.
Q: How do I migrate to backend storage later?
A: The API is the same! Just replace savePageSettings() and getPageSettings() with GraphQL mutations/queries.
Q: Can I save complex objects? A: Yes! The settings are serialized to JSON, so you can save arrays, nested objects, etc.
Files Reference
- lib/pageStore.ts - Zustand store with persist middleware
- lib/types/pagestore.ts - TypeScript types
- components/ux/PageContainer/blocks/GridBlock.tsx - GridBlock component
Related Documentation
- Centralized Page Titles - Centralized page titles
HIERARCHICAL_NAVIGATION_SUMMARY.md- Navigation system (legacy doc, removed in repo cleanup)