Server params - search, filters, sorting, pagination - live in the route query rather than in local refs or a store. A list screen is rarely one component. There's a heading with a search box, a filter bar, the list itself, and all of them need the same page and search values. Put those in the URL and nothing has to sync, each component reads the param it cares about and writes it back, with the route as the single source of truth. Links stay shareable and the back button does what people expect.
Display state is a different thing. Something like a condensed toggle stays a local ref in the view, since nothing outside the page cares about it.
useQueryParam in shared/composables/support wraps a single param as a writable computed. Reading pulls it out of the route, writing pushes a new query. Four behaviors in there are worth knowing, because none of them are obvious from a call site.
A value matching the default drops out of the query instead of being written. Page one never shows up as ?page=1, so the URL only ever carries what actually differs from the defaults.
Setting any param other than page clears page. Changing a filter or a search term means a new result set, and landing on page four of it is almost never what you want.
replace swaps the history push for a replace, which is what search uses. Without it every keystroke stacks a history entry and the back button turns into an undo for typing.
min treats anything shorter than the minimum as unset. Someone can hand edit a two character search into the URL, and if the API rejects it the param reads as empty rather than firing off a request that comes back an error.
Instead of calling useQueryParam at each call site, every resource gets a composable in the app's composables/params folder that declares its full param set in one place, useBookmarkParams, useUserParams, and so on. The list and the heading both call it and get the same refs back.
Defaults come from settings, so the API owns the default sort for a resource and the minimum search length rather than the build. Adding a param is one line in the resource's composable, and the type plus every consumer picks it up from there.
Each app keeps its own params folder because the same resource can mean different things in each. Bookmarks in the app filter by tag and sort, bookmarks in admin only search and paginate.
Settings for where the defaults these composables read come from.