Let's find the work AI should be doing in your organization.
Whether you are exploring an idea or improving a system already in use, we’ll help you decide what is worth doing next.
A frontend is not a coat of paint over the system underneath. It is where API contracts, state, permissions, latency, media and human judgement have to resolve into one predictable experience. We choose the framework after those constraints are known, then engineer every boundary the person using it will eventually meet — including the failure paths.
These are engineering responsibilities before they are framework choices. The tools are where our work has landed in practice; the linked sections explain the contract each layer has to keep.
| Layer | What we use |
|---|---|
| Framework & runtime | SvelteKitNext.jsReact |
| Design system | Design tokensshadcn/uiBits UIRadix UITailwind CSSCVA |
| Contracts & state | OpenAPIOrvalTanStack QueryTanStack FormZod |
| Rendering & BFF | PrerenderingSSRServer ComponentsRoute handlers |
| Operational UI | TablesWizardsOptimistic updatesBackground jobs |
| Tests & quality gates | VitestTesting LibraryMSWPlaywrightAxe |
| Responsive media | Responsive imagesHLStusStreaming UIWebView bridges |
| Production | LighthousePostHogCSPCloudflareAWSCI gates |
We do not choose a frontend framework from a preference list. We choose it from the application’s rendering profile, deployment target, interaction density, existing team and the systems it has to sit beside. The useful decision is not React versus Svelte in the abstract. It is which runtime leaves the product with the fewest accidental seams — between server and browser, content and interaction, the API contract and the component consuming it.
SvelteKit and Next.js are choices, not boundaries. We can dig into any serious frontend framework, inherit one already in production, and become useful without first replacing it. The decision checklist travels; the framework name does not.
A design system is not a gallery of buttons. It is the contract between design decisions and product code: tokens that carry intent, accessible primitives that own interaction, domain components that understand the product, and compositions that let a new screen feel native without copying the old one. We keep those layers distinct so a visual change lands once, while business behaviour stays close to the workflow that owns it.
Tokens carry the visual decisions. Primitives carry interaction and accessibility. Domain components carry the language of the product — a review item, a delivery slot, a source card — and pages arrange them for one task. Mixing those responsibilities is how a component library becomes either too generic to help or too specific to reuse.
We use local primitives first and introduce a shared abstraction only when repetition proves it. That keeps the system coherent without turning every one-off decision into a permanent API.
The frontend should not discover the API by failing against it. We generate typed clients from the schema, keep server data in a query layer with explicit cache and invalidation rules, and separate it from URL state, local interaction state and durable application state. That separation is what lets a screen remain predictable when requests overlap, tabs are restored, permissions change or an optimistic update is rejected.
Generated types do not replace product judgement. They remove contract drift so that judgement can stay focused on what the interface should do when the data is late, partial, forbidden or wrong.
Static, server-rendered and client-rendered are not competing application architectures. They are delivery modes we assign to routes. A marketing page can be generated once, an authenticated workspace can assemble its first response on the server, and the interaction after hydration can stay entirely in the browser. The backend-for-frontend sits where the browser should not: holding credentials, translating sessions, aggregating calls and enforcing cache boundaries.
A BFF is a trust boundary, not a second backend by habit. If it only forwards every request unchanged, it has added a network hop without earning one.
Internal tools are where edge cases become the normal workload. The interface has to keep context across filters, edits and long-running operations; make partial failure legible; and let a person understand what will change before a consequential action lands. We model these workflows explicitly rather than stretching CRUD screens until they almost fit.
The happy path is usually the shortest part of an operational application. The quality of the product shows up in what it lets a person understand, recover and safely try again.
A browser test is too expensive for every branch of logic, and a unit test cannot tell us whether a keyboard user can finish the workflow. We layer tests by responsibility, then make the API simulation work on both sides of the rendering boundary. That last part matters: mocking browser fetch while an SSR loader still reaches a real backend is not an isolated test suite.
We keep traces, screenshots and failure artifacts because a red gate without inspectable evidence only moves debugging from CI to somebody’s laptop.
Responsive engineering is not shrinking the desktop layout. It is choosing what remains visible, what changes interaction model, and what the device should not download at all. Images, video, uploads and streamed interfaces each need their own delivery and recovery contract; otherwise the screen works only on the connection and device it was built on.
The smallest screen is not a lesser product, and the fastest connection is not the baseline. Both are constraints the component should be able to explain in code.
A frontend has production infrastructure even when it deploys as static files. It has secrets and public configuration, browser caches, third-party scripts, security headers, release artifacts and errors that only exist on a customer’s device. We instrument that surface, set budgets before it slows down, and make each deployment small enough to inspect and quick enough to reverse.
We deploy static and edge-ready SvelteKit applications to Cloudflare, and standalone Next.js applications where a Node runtime, Payload or long-running server work belongs. Preview environments carry the same configuration contract as production, smoke tests run against the deployed URL, and rollback stays a release operation rather than a rebuild under pressure.
Where a platform team already owns deployment, we fit its pipeline and produce the evidence it needs. The application should not require a special lane just because its frontend framework has an opinion.
The frontend is not finished when the build passes. It is finished when a failed release is visible, attributable and reversible.
These are not preferences assembled for a stack diagram. They come from running public products, content platforms, data-heavy operational consoles, CMS editors and streamed AI workspaces — across edge deployments, Node applications, browsers and embedded mobile surfaces.
Owning those systems after launch is why the page is opinionated about generated contracts, cache boundaries, resumable media, mobile browser tests, security headers and failure evidence. They are the things that matter when a browser, API or deployment does something the happy path did not predict.
Team Foundry is our software factory — what the team uses day in, day out to deliver projects. It accelerates the work and validates it: every change arrives with the checks it passed, the session that produced it, and a person accountable for it.
How Team Foundry worksWhether you are exploring an idea or improving a system already in use, we’ll help you decide what is worth doing next.
Questions before you book?
Read the FAQFollow along