Tag: TypeScript

  • Frontend Architecture That Survives the Second Year

    Frontend Architecture That Survives the Second Year

    Most frontend codebases are pleasant for the first few months. Then the team grows, features pile up, and suddenly a small change touches nine files and breaks a page nobody was looking at. The decisions that prevent that are rarely about frameworks. They are about boundaries, data flow and habits.

    Organise by feature, not by file type

    A components/ folder with two hundred files, a hooks/ folder and a utils/ folder tells you what things are, not what they are for. Grouping by feature keeps related code together and makes ownership obvious:

    features/
      checkout/
        checkout-page.tsx
        payment-form.tsx
        use-cart-total.ts
        checkout.api.ts
        checkout.test.tsx
      profile/
        ...
    shared/
      ui/        # buttons, inputs, dialogs
      lib/       # formatting, dates, fetch client

    A simple rule keeps this healthy: features may import from shared, but not from each other. When two features need the same thing, promote it to shared deliberately.

    Keep state close to where it is used

    Global state is convenient on day one and expensive on day three hundred. Ask where each piece of state really belongs:

    • Server data belongs in a data layer with caching, such as React Query, SWR or server components, not copied into a global store.
    • URL state such as filters, tabs and pagination belongs in the URL, so it survives refreshes and can be shared.
    • Local UI state such as an open dropdown belongs in the component.
    • Truly global client state, like the current theme or a 3D scene’s settings, is the small remainder that justifies a store.

    When high-frequency values, such as pointer position in a render loop, change every frame, keep them out of React state entirely and read them where they are needed.

    Make components boring to use

    A good shared component has a small, predictable API. Prefer composition to configuration: a Card with slots ages better than a Card with twenty boolean props.

    // Hard to extend
    <Card title="Plan" showBadge badgeText="New" footerAlign="right" />
    
    // Easy to extend
    <Card>
      <Card.Header>Plan <Badge>New</Badge></Card.Header>
      <Card.Footer className="justify-end">...</Card.Footer>
    </Card>

    Let types document the edges

    TypeScript pays off most at boundaries: API responses, component props and shared utilities. Validate data where it enters the app, then trust the types inside. A runtime schema at the API boundary catches backend changes before they become blank screens:

    import { z } from "zod";
    
    const Post = z.object({
      id: z.number(),
      title: z.string(),
      publishedAt: z.string().datetime(),
    });
    
    export type Post = z.infer<typeof Post>;
    
    export async function getPost(id: number): Promise<Post> {
      const response = await fetch(`/api/posts/${id}`);
      return Post.parse(await response.json());
    }

    Test behaviour, not implementation

    Tests that assert on internal state break every refactor and catch few real bugs. Tests that act like a user (type into the field, click the button, expect the message) survive refactors and protect what matters. A practical mix:

    • Unit tests for pure logic: pricing, formatting, reducers.
    • Component tests with Testing Library for important interactions.
    • A handful of end-to-end tests for the flows that make money or keep users safe.

    Automate the arguments away

    Formatting, import order and common mistakes should never come up in code review. A formatter, a strict linter and type checks in CI free reviewers to discuss design and behaviour. Add a performance budget too: a bundle-size check on pull requests catches the accidental 300 KB dependency before it ships.

    Write down the decisions

    The hardest knowledge to recover is why something was built a certain way. A short decision record (context, options, choice and consequences) takes ten minutes and saves hours when someone later wonders whether that odd pattern is safe to remove.

    In short

    • Group by feature; keep cross-feature imports out.
    • Put state where it belongs: server, URL, component, then store.
    • Design components for composition.
    • Validate at the edges, trust types inside.
    • Test what users do.
    • Automate style; document decisions.

    Architecture is not about predicting the future. It is about making the next change easy, and the one after that.

  • Nexu.co

    Nexu.co

    Nexu is a startup platform where users book online consultations with specialists in psychology, medicine and nutrition. I worked on it as a front-end developer for over two years, building the web app that patients use to find a specialist, book a session and manage their care.

    The product

    The web app brings three kinds of consultation into one place. Users browse specialists, compare their profiles, pick a time and attend their sessions online. Everything has to feel calm and trustworthy, because people often arrive at a stressful moment.

    My role

    • Built the user-facing web app with React and Next.js, using TypeScript throughout.
    • Connected the interface to the platform’s REST APIs for specialists, bookings and user accounts.
    • Developed responsive interfaces that work as well on phones as on desktop.
    • Implemented the app as a fast single-page experience to improve performance and usability.

    Tech stack

    • React and Next.js
    • TypeScript
    • Web APIs and REST APIs

    Visit the product at nexu.co.

  • Nexu Dashboard Panel

    Nexu Dashboard Panel

    The Nexu Dashboard Panel is the HR dashboard for Nexu’s partner companies. It lets each company manage its users, their access to consultations and the other settings that come with offering Nexu as an employee benefit. I was the front-end developer on the panel.

    The problem

    Partner companies needed one place to onboard employees, manage their accounts and keep track of how the service was being used, without contacting Nexu support for every change.

    My role

    • Built the full HR dashboard with React, Next.js and TypeScript.
    • Created interactive dashboards with real-time data visualisation.
    • Used MUI for a consistent, accessible component base, and Redux for shared application state.
    • Contributed to a custom UI library that increased developer productivity and code reuse across Nexu projects.
    • Integrated the panel with the platform’s REST APIs.

    Tech stack

    • React, Next.js and TypeScript
    • MUI
    • Redux
    • Web APIs and REST APIs