A browser client should present application data and manage local interaction, while the API owns business decisions. When a site or panel filters records, ranks items, infers permissions, or calculates important totals, those rules become duplicated and may differ across clients. A presentation-only client maps render-ready data into components and handles state that genuinely belongs in the browser, such as an open menu or current focus.
Put derived data in the API
The API can sort posts, calculate counts, select related items, resolve tenant settings, and provide visibility flags. A client should not receive a large raw collection and decide which items are popular or authorized. Doing that work on the server keeps the result consistent across public sites, admin panels, and future clients. It also allows database queries and indexes to limit work before data crosses the network. When the API exposes a `hasItems` or `hasMore` flag, components can render intentionally without inferring application meaning from array lengths.
Keep authorization server-side
Hiding a button is a presentation choice, not a permission check. The API must enforce the rule when an action is requested and return whether the control should be shown. A browser cannot be trusted to protect another tenant's data. The client can display the provided state and a localized error if permissions change between page load and action. Avoid sending raw role matrices and expecting every component to interpret them. A named capability or action flag is easier to render and test.
Preserve useful local state
Presentation-only does not mean static. Menus, disclosures, tabs, dialog focus, unsaved form input, and animation state belong in the client when they cannot be represented as server data. Keep such state close to the component that owns the interaction. Do not copy an entire API response into a second client-side domain model merely to render it. For a list, the client can send filter and page choices to the API and map the returned items; the server applies the filtering, ordering, and count rules. This boundary makes a thin client easier to reason about.
Design the response contract
A render-ready response should contain the labels, URLs, item order, counts, and flags the view needs without forcing several client round trips. It should still avoid sending unnecessary private fields. Decide where localization occurs and keep it consistent. If a layout variant is configured per tenant, the API can resolve the selected variant and fallback while the client renders it. Document response meaning so future components do not recreate the calculation locally. A useful contract is specific enough for the current page and stable enough to evolve.
Test across clients
Compare the same business item in a site and panel when both are meant to show it. If they disagree on status, visibility, or totals, identify which rule moved into the browser. Test tenant-specific settings and locales, including empty states. API integration tests can protect derived values, while component tests focus on rendering and interactions. Performance measurements can show whether the response is too large or too fragmented. The goal is not to put every visual detail in the API; it is to give the client final application decisions.
Reduce long-term change cost
When a ranking or permission rule changes, one server implementation can serve every client. A new theme can reuse the same data contract without copying business logic. For a Software Product Line, this separation is especially valuable because products compose parts differently. Thin clients let small teams change product behavior safely while preserving independent UI work. The browser remains expressive where presentation needs it, and the API remains authoritative where customer data and business meaning are concerned. This contract also improves security review: the team can see where a value was derived and which permission check was applied. Avoid moving a rule back to the browser merely because one screen seems easier to build that way.