App Hooks
All custom hooks live in src/lib/hooks/ and are exported from the barrel at src/lib/hooks/index.ts. Four of the ten hooks come from context files and require their corresponding provider to be in the tree.
Standalone hooks
useDate
Returns a locale-aware date formatter function. Call the returned function with a date string (ISO or similar) and an optional dayjs format token.
The sentinel value PERMANENT_DATE ("2099-12-31") is rendered as a localised "permanent" string rather than a date.
const formatDate = useDate();
formatDate("2026-06-24"); // "June 24, 2026" (locale-dependent)
formatDate("2099-12-31"); // "Permanent"
formatDate("2026-06-24", "MM/DD") // "06/24"
useEditMode
Returns true when the current user is an org admin and the current pathname is under /e. Use it to show or hide edit controls without duplicating the role + path check.
const isEditMode = useEditMode(); // boolean
useIsMobile
Returns true when the viewport is below the given MUI breakpoint. Wraps MUI's useMediaQuery(theme.breakpoints.down(bp)).
const isMobile = useIsMobile(); // below md (900px) — the default
const isPhone = useIsMobile("sm"); // below sm (600px)
Breakpoint convention (see responsive-design.md):
| Breakpoint | Switches | Used for |
|---|---|---|
"md" (default, 900px) | the layout | drawer variant, footer, shell margins |
"sm" (600px) | dialog fullScreen | true phones only |
SSR rule — when to use this hook vs. CSS: the server always renders the desktop value (false); the real value arrives after hydration. Use this hook only where the component structure or behavior must change (the drawer variant, dialog fullScreen). For anything that is purely styling, use CSS breakpoint values in sx instead ({ xs: ..., md: ... }) — those render as media queries on the server with zero hydration flash. Guard visible first-frame flashes with CSS (display: { xs: "none", md: "block" }), never with noSsr.
useReturnTo
Lets "modal" pages (plain page routes rendering a Dialog — see the Navigation note in architecture.md) return to the caller they were opened from, via a returnTo query param, instead of a hardcoded parent path.
const { returnTo, navigateBack, withReturnTo } = useReturnTo();
// In the shell's default Save/Delete/Close handler:
navigateBack("/e/org/partners"); // push returnTo if present & safe, else the fallback
// In ButtonCancel (no fallback):
navigateBack(); // push returnTo if present & safe, else router.back()
// At the link-building call site (attaches the current location):
<ElementChip navLink={withReturnTo(`/e/org/partners/${id}`)} />
| Return value | Meaning |
|---|---|
returnTo | The validated returnTo query param, or null if absent or unsafe |
navigateBack(fallback?) | Pushes returnTo if present; else pushes fallback if given; else router.back() |
withReturnTo(path, extraParams?) | Builds a URL to path carrying the current location (path + query) as returnTo |
Safety rule: returnTo values are validated by isSafeReturnPath (src/lib/url/returnTo.ts) — same-origin relative paths only. Absolute URLs, protocol-relative // values, scheme: values, and values with whitespace are rejected and treated as absent (no open redirect). Values are stored locale-stripped; the i18n router re-prefixes the locale on push. Nested chains round-trip safely because each hop URL-encodes the previous returnTo.
useUrlParams
Builds a memoized query-parameter object from the current URL state (powered by nuqs). Strips empty and undefined values automatically.
const params = useUrlParams({ noOu: true, forceActive: true });
// { query: "gdpr", role: 2 }
| Option | Effect |
|---|---|
noOu | Excludes the ou (organisational unit) filter |
noSearchTerm | Excludes the query search filter |
forceActive | Never includes showInactive |
forceInactive | Always includes showInactive=true |
Context hooks
These hooks read from a React context. Using them outside their provider throws (or returns a fallback — see notes per hook).
useOrganization
Returns the current OrganizationType from OrganizationProvider. Throws if used outside the provider. Use this in any component that requires org data to render.
const org = useOrganization();
useOrganizationOptional
Same as useOrganization but returns a MOCK_ORGANIZATION fallback instead of throwing when used outside the provider. Use this in components that live inside the event layout or other public routes where OrganizationProvider is not guaranteed to be in the tree — MainNavBar, LanguagePicker, and the sign-out page are its callers.
const org = useOrganizationOptional();
useRopa
Returns the current RopaType (Records of Processing Activities data) from OrganizationProvider. Throws if used outside the provider.
const ropa = useRopa();
useUserRole
Returns the current user's role within the organisation, or null if unauthenticated.
const role = useUserRole(); // "admin" | "member" | null
useDrawer
Returns the sidebar drawer open state and its setter from DrawerProvider. Returns a no-op fallback ({ open: false, setOpen: () => {} }) when used outside the provider.
const { open, setOpen } = useDrawer();
The meaning of open depends on the viewport (see responsive-design.md): from md up the drawer is the permanent mini-variant and open means "expanded"; below md it is a temporary overlay and open means "overlay visible". Any drawer item whose click navigates must call setOpen(false) when mobile.
Providers
| Provider | File | Supplies |
|---|---|---|
OrganizationProvider | OrganizationContext.tsx | useOrganization, useOrganizationOptional, useRopa, useUserRole |
DrawerProvider | DrawerContext.tsx | useDrawer |