Skip to content
← Back to blog

Article · Blog

The frontend is where the product gets judged

I own the AI, the app, the TypeScript backend, and the release — and the interface is still where users decide whether the product is any good.

The frontend is where the product gets judged

Over the past few years, I’ve tried a lot of technologies. I kept judging them by two things:

  1. Do they help me improve the user experience?
  2. Do I find the work interesting?

I own the AI, the app, the TypeScript backend, and the release. None of that is what a user meets. They meet an interface, and that is where they decide whether the product is any good.

Why the interface gets the attention

The projects I felt proudest of were usually the ones where I had taken time over the interface and its interactions. An elegant API cannot rescue a frontend that feels slow or confusing, and neither can a good model sitting behind it.

In Next for Vercel, Guillermo Rauch applies Bezos’s obsessive customer focus at Amazon to web development: “focusing on the frontend is the most customer-obsessive way to build your products.” That is why the surface gets a disproportionate share of my attention, even on the products where I wrote every layer beneath it.

Users interact with the design, content, colours, typography, layout, animation, and responsiveness. They do not see the database schema, the inference pipeline, or the service architecture behind them. They notice whether the product is clear, quick, and pleasant to use, and the frontend shapes how they perceive the brand. From their side of the screen, the frontend is the product.

The tools that shaped my work

Several frameworks and tools have changed how I build for the web.

React was my starting point. Its component model changed how I thought about interfaces. Building larger screens from small, reusable pieces felt natural, and I spent months working with Create React App before moving to other setups.

Next.js was the next step. Server rendering, static generation, API routes, and file-based routing solved problems I was running into with SEO, initial load performance, and manual webpack and Babel configuration. I built my previous portfolio, Cartes.dev, with Next.js and ran it for years.

Remix made me reconsider how web applications should work. It leans on HTML forms, HTTP caching, and the browser’s own capabilities. I used it to build a full stack application with a separate Express backend. That project taught me where a framework helps and where the web platform already has a good answer.

TailwindCSS changed how I write styles. I was sceptical at first. After years of keeping CSS separate, utility classes in markup looked wrong. A week on a real project changed my mind. It sped up iteration because I could work directly in the component instead of switching files, naming classes, and dealing with specificity:

<div className="flex items-center gap-4 rounded-lg bg-gray-900 p-6 shadow-xl">
  <h2 className="text-2xl font-bold text-amber-400">
    Build faster, iterate quicker
  </h2>
  <p className="text-gray-300 leading-relaxed">
    Utility-first CSS removes the friction between thinking
    about design and implementing it.
  </p>
</div>

With Tailwind and React, the styles stay close to the component that uses them. That makes refactoring easier and leaves less unused CSS behind.

Astro is the latest addition to my toolkit and the framework behind this portfolio. Its islands architecture sends no JavaScript by default and hydrates only the components that need it. That suits a portfolio or blog: most pages stay light, while I can still use React where interaction calls for it.

Start with the user

Good user experience starts by getting the main content and functionality onto the screen quickly, ready for someone to use. Everything else comes after that.

I tend to think about speed first. Then I ask whether the screen makes sense. Finally, does using it feel polished and enjoyable?

Next.js and Astro can improve delivery through server rendering and static generation. Design systems and component libraries make interfaces more consistent. Animation and small interaction details can add polish, as long as they do not get in the user’s way.

Performance numbers that matter

Google’s PageSpeed Insights points to useful measures such as First Contentful Paint (FCP), Interaction to Next Paint (INP), Largest Contentful Paint (LCP), and Cumulative Layout Shift (CLS). The Core Web Vitals documentation explains them in more detail.

These metrics describe things people notice: how soon they can read, how quickly the page responds, and whether the layout moves beneath them. Studies have linked a one-second load delay with a significant drop in conversions. I care about the score because it points to those experiences, not because the dashboard itself matters.

These are some of the techniques I use most often:

  • Use WebP or AVIF, responsive srcset values, and lazy loading for images below the fold. On image-heavy pages, this can cut seconds from the load time.
  • Self-host fonts, set font-display: swap, and subset character sets where it makes sense. Subsetting can reduce font file sizes considerably.
  • Split JavaScript by route or component with tools such as React.lazy() and Suspense.
  • Reserve space for images and dynamic content so the layout stays put while they load.
// Lazy loading a heavy component to improve initial bundle size
import { lazy, Suspense } from "react";

const HeavyChart = lazy(() => import("./components/HeavyChart"));

function Dashboard() {
  return (
    <Suspense fallback={<div className="h-64 animate-pulse bg-gray-800 rounded-lg" />}>
      <HeavyChart />
    </Suspense>
  );
}

I use this pattern for components that start off screen or bring in large dependencies. It keeps them out of the initial JavaScript bundle and makes the first paint faster.

Frontend craft, whole-product ownership

Caring most about the frontend does not mean handing the rest to someone else. On the products I build, I write the backend, the data model, the AI pipeline, and the release process too, and each of those decides how the application handles data, state, performance, and stability.

What it decides is where the remaining attention goes once the system works. A full stack engineer needs to understand both sides, especially how they interact. My preference is to use stable, familiar backend technologies that perform well at a low cost, then spend more of my attention on what the user sees and does.

I do not want to reinvent authentication or database connection pooling when established solutions exist. PostgreSQL, Redis, Node.js, and Cloudflare Workers have years of production use and documentation behind them, and they can scale with a project. Choosing familiar backend tools leaves me more time for the interface, where I think the product can stand out.

Backend knowledge still makes me a better frontend developer. It helps me reason about APIs, query performance, server caching, data fetching, optimistic updates, and error handling.

Frontend patterns I keep using

I keep coming back to a few patterns.

Compose components instead of configuring one giant component. I prefer small pieces that fit together over a monolith with dozens of props. The result is easier to understand, test, and change:

// Prefer this composable approach
<Card>
  <Card.Header>
    <Card.Title>Project Name</Card.Title>
    <Card.Badge variant="success">Live</Card.Badge>
  </Card.Header>
  <Card.Body>
    <Card.Description>A brief project summary.</Card.Description>
  </Card.Body>
  <Card.Footer>
    <Card.Link href="/portfolio/project">View Project</Card.Link>
  </Card.Footer>
</Card>

Move reusable logic into custom hooks. This keeps React components focused and lets the same behaviour work elsewhere in the application.

Build with progressive enhancement. I start with HTML and CSS, then add JavaScript where the interaction needs it. The core experience can still work if JavaScript fails or takes time to load, and the result is usually more accessible and faster.

What is changing the work now

A few shifts have already changed how I build, rather than sitting on a list of things to watch.

Server components in React, and the same idea in other frameworks, put data fetching and HTML rendering on the server alongside interactive client components in the same tree. That moved real work off the browser and changed how I split a page.

Running code close to the user is the second one. Cloudflare Workers put server logic, and now model inference, a few milliseconds from the request instead of hundreds. That changes both what is worth rendering on the server and what is worth asking a model for while someone waits for the screen.

The browser also keeps absorbing what used to need a library. Container queries, the :has() selector, view transitions, and the Popover API have each replaced JavaScript I would otherwise have shipped.

Browse my full portfolio to see my latest work.

Have a project in mind?

Tell me what you're working on and where you need help.