Decisions that shape a frontend project
Four decisions in week 1 set the next two years:
- Framework - Next.js, Astro, plain React + Vite. Pick per product, not per trend.
- Rendering - Server Components for read-heavy pages, client islands for interaction, streaming for anything over 100ms.
- Styling - Tailwind + design tokens (our default), or a CSS-in-JS runtime (rarely). The ergonomics win of CSS-in-JS rarely earns its performance tax on new builds.
- Deploy target - Vercel, Netlify, Cloudflare Pages, self-hosted. We meet your infrastructure.
Get these right and every feature is cheaper. Get them wrong and you are fighting the bundle size for two years.
What you actually want from a modern frontend
Speed on real networks, not localhost. Accessibility without an accessibility consultant. Design tokens that survive a rebrand. A component system that lives in the repo, not in a library you cannot change. Tests that run in CI and block merges. Deploys that roll back in one click.
Every one of those is boring. All of them matter more than the framework brand.
Which framework, when
Astro - content-first pages. Marketing, docs, blogs, landing pages, anything the user mostly reads. Zero JS shipped by default. Islands hydrate only what actually interacts. LCP usually under 500ms on cable, under 1.2s on 4G throttled. This site you are reading is built this way.
Next.js App Router - app shells. Dashboards, auth-gated products, server-rendered pages with real data. The React Server Components + streaming + Server Actions combination is the most honest modern React setup. We pick it for anything that is an app, not a page.
Plain React + Vite - admin panels, internal tools, heavy client apps. When the thing is genuinely an SPA (rich editor, data-grid tool) and server rendering buys nothing, we skip Next.js overhead and ship a Vite SPA.
We pick and explain. Tell us the product and we tell you which framework we would pick and why.
Core Web Vitals as a design discipline
Fast pages are not an optimization at the end. They come from decisions made early:
- LCP (largest contentful paint) - preload the hero image and hero font. No layout shift for above-the-fold content.
- INP (interaction to next paint) - keep hydration islands small. Defer non-critical JS to idle.
- CLS (cumulative layout shift) - always set image width and height. Reserve space for anything that loads late.
We instrument CWV with Vercel Analytics or a self-hosted alternative. The dashboard tells us when a deploy regressed something. Regressions are caught and fixed, not discovered from a user complaint.
Accessibility, not theatre
Every ship passes a keyboard-only pass and a screen-reader pass (VoiceOver + NVDA). We use Radix under shadcn because the ARIA is done correctly upstream. axe-core runs on every PR. Contrast ratios are part of the design token system, not a last-minute fix.
Accessibility is not a compliance checkbox. An app that works with a keyboard works for a user holding a baby, a user with tendonitis, a user on a train, and a user behind a screen reader.
Tailwind + shadcn/ui - why this is the right default
Most component libraries (MUI, Chakra, Ant Design) solve the “I need a button” problem and create two new problems:
- The styles live in node_modules. Changing a border-radius means fighting the library or shipping CSS overrides.
- The bundle bloats. Even with tree-shaking, a component library is 80 to 200KB of CSS + JS you did not write.
shadcn/ui gives you Radix primitives (which handle ARIA correctly) styled with Tailwind in your repo. Want to change the button? Edit the file. No library upgrade path breaks your design.
Case studies
- Al Rajhi-custodied gold trading across iOS, Android, and web - Flutter Web admin dashboard alongside mobile apps, live Socket.IO price feed rendering identically across devices, multi-timeframe historical charts. Shows the design challenge of financial UI at 60fps with real-time data.
- Trading calculators with live prices wired in - custom chart rendering via Flutter CustomPainter, multi-timeframe views with tick-by-tick updates. The same design challenges apply on the web: off-the-shelf chart libraries run out of road for trading-terminal UI, and hand-built rendering wins for fidelity.
How we work on a frontend engagement
Week 1 - architecture. Page list, data boundaries, server-vs-client split, auth strategy, deploy target, analytics plan. Written down and reviewed before a single JSX line.
Weeks 2 to 3 - the spine. Shell, auth, design-token system, first real page rendered. Preview URL live by Friday of week 2. Product reviews happen on real URLs, not localhost.
Weeks 4 to 10 - pages, one per week. Lighthouse runs on every PR. Regressions block merge. Playwright covers critical flows end to end.
Weeks 11 to 12 - polish and launch. Accessibility pass, analytics, error boundaries, Core Web Vitals dashboards, SEO validation.
Design systems and component libraries
- Design tokens in CSS variables - one source of truth for colour, spacing, type scale, radii, shadows.
- shadcn/ui on Radix primitives - copy-in components, not a dependency. ARIA correct upstream.
- Storybook for component libraries that need isolated review across teams.
- Figma design tokens synced to CSS variables via a token pipeline when the design team changes often.
Deploys, CI, and rollbacks
- GitHub Actions - type-check, unit tests, Playwright against preview on every PR.
- Preview deploys per branch. Product reviews happen on real URLs.
- Production behind a protected branch. Manual gate before promote.
- One-click rollback. vercel rollback or equivalent. We test the rollback in the first week, before we need it.
What we will not do
- jQuery-era SPAs. If the build is a static page, it ships as a static page.
- Webflow as the final answer. Fine for a 4-week marketing MVP. Not fine for a product that needs to grow.
- Bootstrap in 2026. Tailwind + design tokens does it better in less markup.
- CSS-in-JS runtimes on new builds. The performance tax is real. The ergonomics win is marginal against Tailwind + CSS variables.
Why teams pick our frontend delivery
We pick the framework to match the product: Astro for content, Next.js for apps, plain React + Vite for SPAs. Opinionated, but not married to any of them. If your product is three mostly-static pages, we will talk you out of Next.js.
Core Web Vitals go on the dashboard from week 1, not after a user complaint. Accessibility is engineering: Radix for ARIA, axe-core on every PR, keyboard pass before every ship. Design tokens live in CSS variables so a brand colour change moves one line, not twelve files. Senior engineers on the product. Source and design files yours at launch.
Read next
- React Development - the deep dive on React + Next.js.
- Backend Development - the typed API the frontend talks to.
- AI Integration Services - adding LLMs and streaming into an existing frontend.