Hash-based Navigation
eziwiki uses hash-based URLs for instant, client-side navigation without page reloads.
How It Works
Traditional URLs:
https://wiki.example.com/getting-started/installation
https://wiki.example.com/configuration/themeHash-based URLs:
https://wiki.example.com/#2c8f9a1b
https://wiki.example.com/#7d3e4f2aEach content path is converted to a unique hash, enabling:
- Instant navigation - No page reloads
- Persistent state - Sidebar state preserved
- Browser history - Back/forward buttons work
- Bookmarkable - Share direct links to pages
Benefits
Lightning Fast
No server requests needed. Content loads instantly from the static bundle.
// Traditional navigation
window.location.href = '/page'; // Full page reload
// Hash navigation
window.location.hash = '#abc123'; // Instant, no reloadState Preservation
Your sidebar state (expanded/collapsed sections) persists across navigation:
// Sidebar state stored in localStorage
{
"sidebar-state": {
"getting-started": true, // Expanded
"configuration": false // Collapsed
}
}Works Offline
Once loaded, the entire site works offline. No network requests for navigation.
View All URLs
Use the built-in script to see all hash URLs:
npm run show-urlsOutput:
š Hash-Based URLs
================================================================================
š intro
ā /2c8f9a1b
ā https://eziwiki.dev/2c8f9a1b
š getting-started/quick-start
ā /7d3e4f2a
ā https://eziwiki.dev/7d3e4f2a
š [HIDDEN] secret-page
ā /9f1a2b3c
ā https://eziwiki.dev/9f1a2b3c
================================================================================
Total pages: 15
Hidden pages: 1Hidden Pages
Pages marked as hidden: true don't appear in the sidebar but are still accessible via their hash URL:
// payload/config.ts
navigation: [
{
name: 'Secret Page',
path: 'secret-page',
hidden: true, // Not in sidebar
},
];Use cases:
- Draft pages
- Unlisted content
- Internal documentation
- Work-in-progress pages
Find hidden page URLs:
npm run show-urls | grep HIDDENInternal Links
Write normal Markdown links - they're automatically converted to hash URLs:
Check out the [Quick Start Guide](/getting-started/quick-start).Becomes:
<a href="#7d3e4f2a">Quick Start Guide</a>Technical Details
Hash Generation
Paths are hashed using a deterministic algorithm:
import { generatePathHash } from '@/lib/navigation/hash';
const hash = generatePathHash('getting-started/quick-start');
// Returns: "7d3e4f2a"Navigation State
The current page is tracked in Zustand store:
import { useTabStore } from '@/lib/store/tabStore';
const { activeTab, setActiveTab } = useTabStore();
// Navigate to a page
setActiveTab('getting-started/quick-start');Browser Integration
Hash changes update browser history:
// Navigate forward
window.location.hash = '#7d3e4f2a';
// Navigate back
window.history.back();
// Listen to changes
window.addEventListener('hashchange', () => {
const hash = window.location.hash.slice(1);
// Load content for hash
});SEO Considerations
Hash-based navigation is client-side only. For SEO:
- Static Export: All pages are pre-rendered as HTML
- Meta Tags: Each page has proper meta tags
- Sitemap: Generate a sitemap for search engines
- Open Graph: Social sharing works correctly
Search engines see the full content, users get instant navigation.
Comparison
| Feature | Hash Navigation | Traditional |
|---|---|---|
| Speed | Instant | Page reload |
| State | Preserved | Lost |
| Offline | Works | Requires server |
| Bundle Size | Larger | Smaller |
| SEO | Pre-rendered | Native |
Best Practices
Use Descriptive Paths
ā
Good:
{ name: 'Quick Start', path: 'getting-started/quick-start' }
ā Bad:
{ name: 'Quick Start', path: 'qs' }Descriptive paths make debugging easier.
Organize by Folder
content/
āāā getting-started/
ā āāā installation.md
ā āāā quick-start.md
āāā configuration/
ā āāā payload.md
ā āāā theme.mdFolder structure reflects navigation hierarchy.
Test Navigation
# Start dev server
npm run dev
# Test all links
# Click through your navigation
# Use browser back/forward buttons
# Refresh pages
# Test bookmarksTroubleshooting
Page Not Found
If a hash URL doesn't work:
- Check the path exists in
payload/config.ts - Verify the Markdown file exists
- Run
npm run show-urlsto see all valid URLs - Check browser console for errors
State Not Persisting
If sidebar state doesn't persist:
- Check localStorage is enabled
- Clear browser cache
- Check for console errors
- Verify Zustand store is working
Links Not Working
If internal links don't work:
- Use absolute paths:
/getting-started/quick-start - Don't use relative paths:
../quick-start - Match paths exactly as in
payload/config.ts