Performance
Remove unnecessary query stages, reduce payloads, and measure the Nuxt-to-Convex lifecycle.
Optimize the lifecycle you actually use. Do not add another cache before identifying the expensive stage.
Recommended order
- Measure SSR, navigation, payload size, and subscription activity.
- Move browser-specific data out of SSR when it does not improve initial HTML.
- Return less data and paginate growing collections.
- Avoid unnecessary auth work on public paths.
- Tune deployment and auth verification from evidence.
Remove unnecessary SSR
Use server: false for browser-specific data that does not improve the initial HTML.
useConvexQuery(api.presence.current, {}, { server: false })The cost is a browser loading state and no server-rendered value.
Use imperative calls for snapshots
Composable query state is live by design. For a one-shot operator action or snapshot, call through the restricted client handle instead of mounting reactive state:
const report = await useConvex().query(api.reports.monthly, args)Keep public data independent of auth
auth: 'none' skips auth settlement and always uses anonymous transport. Use it only for functions whose result and authorization are truly identity-independent.
Bound result size
Return fields the page uses. Use indexes and take() for bounded lists. Use pagination for growing collections. Large SSR results increase Convex work, HTML/payload size, parsing, and hydration cost.
Understand duplicate dashboard calls
The default SSR-to-live lifecycle creates one server HTTP query and one browser subscription. This is not a duplicate subscription bug. Disable a stage only when its product benefit is unnecessary.
Avoid a mirrored store
Copying Convex entities into Pinia adds update and identity-clearing work. Use normal queries and keep stores for UI-only state.
Measure production behavior
Development includes HMR, DevTools, source maps, and extra diagnostics. Use production builds and representative deployment regions before drawing latency conclusions.