← Our services Web applications · technically

The interface is where every system promise becomes real.

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.

The implementation map

What we build through.

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.

LayerWhat we use
Framework & runtimeSvelteKitNext.jsReact
Design systemDesign tokensshadcn/uiBits UIRadix UITailwind CSSCVA
Contracts & stateOpenAPIOrvalTanStack QueryTanStack FormZod
Rendering & BFFPrerenderingSSRServer ComponentsRoute handlers
Operational UITablesWizardsOptimistic updatesBackground jobs
Tests & quality gatesVitestTesting LibraryMSWPlaywrightAxe
Responsive mediaResponsive imagesHLStusStreaming UIWebView bridges
ProductionLighthousePostHogCSPCloudflareAWSCI gates
Framework & runtime

Choose the application framework after the constraints are known.

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.

What we decide before naming a framework
  • Which routes can be static, which need server rendering, and which are interaction-first
  • Whether the application runs at the edge, on a Node server, or inside an existing platform
  • How tightly the frontend should share types and runtime code with its backend
  • Whether content editing, commerce, streaming or an embedded WebView changes the shape
  • What the team already knows and will still be able to maintain after handover
  • Which ecosystem capabilities are genuinely needed rather than merely available
  • What the framework adds to the browser bundle, build pipeline and operational surface
Where our defaults land today
  • SvelteKit for lean products, content-heavy applications and edge-ready delivery Svelte 5SvelteKitCloudflare
  • Next.js where React, Payload, Server Components or streaming interfaces earn their weight ReactApp RouterPayload
  • Incremental adoption when a sound application already exists Inherited codebaseMigration in slices

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.

Design system

Build the shared language before the page count grows.

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.

  • Semantic tokens for colour, type, spacing, motion and elevation CSS custom propertiesDesign tokens
  • Accessible primitives for focus, keyboard control, overlays and form semantics shadcn/uiBits UIRadix UI
  • Variants expressed as typed component APIs rather than class-name conventions CVATailwind Variants
  • Domain components for the repeated product decisions Typed propsComposition
  • Loading, empty, error, disabled and destructive states designed with the default state State matrix
  • Responsive behaviour owned by the component that changes Container queriesMobile first
The ownership layers

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.

Contracts & state

Make the data contract hard to misuse.

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.

  • Generate request, response and error types from the API contract OpenAPIOrval
  • Give every server read a stable key, lifetime and invalidation path TanStack QueryQuery keys
  • Cancel stale requests when navigation or filters move on AbortSignal
  • Keep filters, pagination and shareable selections in the URL URL state
  • Model complex forms with typed validation and field-level errors TanStack FormZod
  • Use optimistic updates only when the reconciliation path is defined Optimistic UIRollback
  • Preserve one error envelope from transport to the component rendering it Structured errors

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.

Rendering & BFF

Choose the rendering boundary route by route.

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.

The rendering map
  • Generate stable content and CMS routes at build time PrerenderingStatic output
  • Render authenticated or SEO-sensitive first responses on the server SSRServer Components
  • Hydrate only the interaction that needs browser state Client componentsProgressive enhancement
  • Stream slow results without blocking the whole response StreamingSuspense
What belongs in the BFF
  • Session cookies, OAuth and OTP exchanges that must not expose private credentials
  • Permission-aware aggregation across APIs the browser should not call directly
  • Turnstile and other server-verified anti-abuse controls
  • Media signing, upload completion and safe proxying
  • Per-user cache control and revalidation headers
  • Protocol translation where the product needs a smaller, stable surface

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.

Operational UI

Design for the work including when it goes wrong.

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.

  • Data tables with URL-backed filters, sorting and pagination TanStack TableSaved views
  • Multi-step workflows that can be resumed without re-entering known data WizardsDraft state
  • Bulk operations that preview scope before mutation Dry runsSelection models
  • Long-running imports, exports and processing shown as durable jobs Background jobsPolling
  • Review queues that preserve provenance and the reason something needs attention Review stateAudit trail
  • Partial failures reported per item, with a safe retry path IdempotencyRetry
  • Empty, stale, forbidden and degraded states treated as product states State matrix

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.

Tests & quality gates

Test each boundary with the cheapest useful tool.

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.

The test layers
  • Pure rules, transformations and state machines Vitest
  • Components against a real DOM and browser events Testing LibraryVitest Browser
  • Deterministic APIs across browser fetch, SSR loaders and the BFF MSWServer handlers
  • Money paths across desktop and mobile browser engines PlaywrightChromiumWebKit
What can block a release
  • New serious or critical accessibility violations
  • A regression in the critical user journeys
  • An API contract that no longer generates or type-checks
  • A performance or bundle budget crossing its reviewed limit
  • Unexpected console, network or hydration errors
  • A visual change outside the approved component or page scope

We keep traces, screenshots and failure artifacts because a red gate without inspectable evidence only moves debugging from CI to somebody’s laptop.

Responsive media

Treat the viewport and the network as runtime inputs.

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.

  • Components own the breakpoint where their interaction changes Container queriesMedia queries
  • Images carry dimensions, responsive sources and modern formats srcsetCDN transformsPlaceholders
  • Large uploads use signed destinations, checksums and resumable transfer Signed URLsSparkMD5tus
  • Adaptive video playback follows connection and device capability HLS.jsMedia sessions
  • Structured UI streams recover from interruption without losing the resolved state ReadableStreamEvent protocol
  • Animation responds to reduced motion and never blocks the task RiveGSAPprefers-reduced-motion
  • The same web surface can run inside an explicit native bridge WebView bridgeShared tokens

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.

Production

Operate the frontend after it ships.

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.

Security and trust
  • Authentication and authorization enforced at server boundaries HttpOnly cookiesScoped roles
  • Browser capabilities restricted to what the application needs CSPSecurity headers
  • Public configuration separated from server-only secrets Environment contract
  • Abuse constrained before expensive work begins Rate limitsTurnstile
Performance and observability
  • Core Web Vitals and bundle size tracked across releases Lighthouse CIBundle analysis
  • Client and server exceptions captured with safe, redacted context PostHogStructured logs
  • Product events defined as schemas rather than ad hoc strings Event contracts
  • Feature changes isolated behind observable rollout controls Feature flags
Delivery

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.

Where this comes from

We operate the interfaces after handover.

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.

What we have built
How we work

An AI-native team, with a factory behind it.

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 works
Factory

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.

Prefer to start async?

hello@thoughtfulrobots.ai

Hyderabad · Remote

Questions before you book?

Read the FAQ

Follow along

START A CONVERSATION

Tell us about the work.

A few lines about your product and where AI might belong. We usually reply within one business day.