Admin performance
Keep custom Svelte components responsive and control the JavaScript bundled into Ridu’s embedded admin.
The built admin is static browser code embedded in the Go binary. A separate JavaScript server is not required in production, but every application component and imported browser dependency still has download, parse, and interaction cost. Measure the production build in a browser; development hot reload is designed for feedback speed and is not a bundle-size result.
Match the extension to the job
| Need | Registration | Performance boundary |
|---|---|---|
| Change one form control | fields['app:name'] = defineFieldEditor(...) |
Runs with that field occurrence; keep input handling local. |
| Summarize a repeated row | rowLabels['app:name'] = defineRowLabel(...) |
Can rerender while unsaved row values change; avoid scanning unrelated rows. |
| Format a collection cell | listCells[] |
Runs for every visible cell; render supplied data instead of fetching per cell. |
| Add document UI | documentActions[] or documentViews[] |
Receives one saved document; call the SDK only for a deliberate user action or needed view. |
| Add application navigation/page content | navigation[], dashboard[], or routes[] |
Statically registered and included in the application admin build. |
| Share Svelte context | providers[] |
Wraps the complete admin; do only setup that every screen needs. |
Use the smallest extension surface that owns the behavior. A provider that fetches data for one page runs more broadly than a request started by that page. A list cell that makes its own request multiplies calls by visible rows; include needed data in the list selection or use a custom view when the interaction needs a separate query.
Keep field updates local
A field editor receives a stable binding for its current occurrence. Read field.value, update it
with field.set(...), and use form.get(path) only for values the control actually depends on.
Do not mirror the complete form into another state object or rebuild every row on each keystroke.
| Field API | Use |
|---|---|
field.value |
Current unsaved value for this occurrence. |
field.set(value) |
Update this field through the form controller. |
field.inputProps |
Connect the control to its label, description, and validation messages. |
field.readOnly |
Disable or mark the control read-only when editing is not allowed. |
form.get(path) |
Read one other unsaved field when the control depends on it. |
field.stale |
Discard an asynchronous result after navigation, row removal, locale change, save, or reset. |
field.liveValidation |
Display Ridu’s current advisory server-check state. |
The built-in live validation controller cancels obsolete requests. For a custom request, keep an
AbortController, abort it when the input changes or the component is destroyed, and check
field.stale before applying the response. Trigger expensive requests after a short pause, on
blur, or from a button instead of on every keypress.
Avoid per-row network requests
List cells, row labels, and repeated field components may appear many times. Use data already provided in props for display. When a screen needs related data, prefer one bounded SDK request and index the result by document ID rather than starting one request from every component instance.
Relationship and upload controls already use paginated, access-checked browsers. Keep option queries bounded and filter on the server; do not download the whole target collection to filter it in Svelte.
Control browser dependencies
Imports in admin/src/admin.config.ts and the components it references are part of the static
admin module graph. Before adding a large editor, charting library, date package, or SDK, check
whether browser APIs or @riducms/ui already cover the task. Import the focused module you use
rather than a package-wide namespace when the dependency supports it.
Build the same artifact you will deploy:
bun run buildInspect emitted JS/CSS sizes and test the route that loads the extension with browser performance tools. Record compressed transfer, parse/evaluation time, long tasks, and the interaction you care about. The Go binary size includes embedded assets, but binary size alone cannot tell you how much JavaScript a browser downloads for one route.
Test realistic author interactions
Use documents large enough to expose repeated-field behavior and target collections large enough to exercise picker pagination. Measure typing, opening drawers, reordering, saving, switching locales, and loading rich text separately; one fast initial HTML response does not prove those interactions are fast.
Run the application checks after changing an extension, then use
Measure performance for comparable browser and server results.
The repository’s controlled browser thresholds run separately in make performance-check.