Skip to main content

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):

BreakpointSwitchesUsed for
"md" (default, 900px)the layoutdrawer variant, footer, shell margins
"sm" (600px)dialog fullScreentrue 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 valueMeaning
returnToThe 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 }
OptionEffect
noOuExcludes the ou (organisational unit) filter
noSearchTermExcludes the query search filter
forceActiveNever includes showInactive
forceInactiveAlways 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

ProviderFileSupplies
OrganizationProviderOrganizationContext.tsxuseOrganization, useOrganizationOptional, useRopa, useUserRole
DrawerProviderDrawerContext.tsxuseDrawer