Repository navigation
url state
Every admin page serializes its filter, tab, search, and cursor state into URL search params. This means any admin view is directly shareable by copying the browser URL.
| Param | Type | Purpose | Example |
|---|---|---|---|
?tab=<name> |
string | Active tab on a multi-tab page | ?tab=metrics |
?q=<text> |
string | Search query | ?q=postgres |
?since=<duration> |
string | Time-range filter | ?since=1h |
?filter.<key>=<value> |
string | Arbitrary filter | ?filter.status=active |
?cursor=<opaque> |
string | Pagination cursor | ?cursor=eyJpZCI6MTAwfQ |
?selected=<id> |
string | Selected row ID | ?selected=abc123 |
Multiple params may be combined: /monitor?tab=metrics&since=1h
import { useUrlState } from '@/hooks/useUrlState'
// Tab state — syncs ?tab= to URL
const [activeTab, setActiveTab] = useUrlState<string>('tab', 'overview')
// Search with 300ms debounce — syncs ?q= to URL
const [query, setQuery] = useUrlState<string>('q', '', { debounce: 300 })
// Time range — syncs ?since= to URL
const [timeRange, setTimeRange] = useUrlState<string>('since', '1h')| Option | Default | Description |
|---|---|---|
pushMode |
'replace' |
'replace' = no back-stack entry; 'push' = adds browser history entry |
debounce |
0 |
ms to wait before updating URL (use 200-400 for text inputs) |
useUrlState depends on Next.js useSearchParams, which requires the component to be wrapped in a <Suspense> boundary. If your page renders directly without a content component, wrap it:
function MyContent() {
const [tab, setTab] = useUrlState('tab', 'overview')
// ...
}
export default function MyPage() {
return (
<Suspense>
<MyContent />
</Suspense>
)
}Every page using <PageHeader> automatically includes a Copy link button in the top-right corner. Clicking it:
- Copies the full current URL (including all search params) to the clipboard.
- Shows a toast: "Link copied. Recipient must be signed in to view."
The button is hidden in print mode. To suppress it on a specific page, pass showCopyLink={false} to <PageHeader>.
| Group | Routes | Params | Status |
|---|---|---|---|
| Tab pages | services/*, monitor, notifications, reports, help, backup, plugins/[name], database/inspect, database/data, k8s/status, tools/webhooks, claw/sessions | ?tab= |
Done (P95 Y01) |
| Search pages | plugins, help/search, helm/values, logs |
?q=, ?filter.*
|
Done (P95 Y01) |
| Time-range pages | monitor | ?since= |
Done (P95 Y01) |
| Seed pages (P94 S35) | logs, plugins, services | Native useSearchParams
|
Done (P94 S35) |
Admin is intentionally single-locale (en). next-intl [locale] segment routing is not used. If a second locale is added in a future version, convert to [locale] segment routing per next-intl docs at that time.
All admin pages include @media print styles in src/styles/tailwind.css that:
- Hide sidebar, topbar, nav, action buttons, tooltips
- Expand main content to full width
- Set font size to 11pt for A4/Letter compatibility
- Prevent overflow clipping
Version: 1.0.0 | Updated: 2026-10-19 11:40 UTC | GitHub