Web applications, built with Next.js.
Server-rendered React applications that load fast, rank well, and hold up in production. Built with Next.js, TypeScript, and PostgreSQL.
Search visibility
Server-side rendering and static generation mean search engines index real HTML. Your content is crawlable from day one.
Core Web Vitals
Image optimization, font loading, and code splitting are standard. We target green Lighthouse scores on every release.
One codebase, front to back
Next.js API routes let us build the backend and frontend together, which keeps complexity and server costs down.
A web application is not a website.
A brochure site is content you publish. A web application is a system people log into and work in: accounts and roles, data that changes hour by hour, rules about who may see and change what, and a record of what happened. The pages are the visible part. Most of the build sits behind them.
What is usually in scope and rarely in the brief
- Accounts: sign-up, sign-in, password reset, email verification, invitations, and roles that decide what each user can reach.
- A data model that survives contact with reality — the entities, how they relate, and the states a record moves through.
- An admin surface for your own team: finding a record, fixing bad data, refunding, suspending, exporting. Somebody always needs it on day two.
- Background work: scheduled jobs, imports, exports, document generation — anything too slow to run inside a request.
- Transactional email and notifications, sent from a domain authenticated properly enough that the mail actually arrives.
- File upload and storage, with size limits, type checks and access rules that apply to the file itself, not only to the page that links to it.
- Payments, if money moves: idempotent writes, webhook handling, refunds, and a record you can reconcile.
- Environments, deploys, backups, logs and error tracking — the parts nobody demos and everybody needs at two in the morning.
We write this list down before the estimate, because it is where estimates go wrong. A screen count tells you almost nothing about the size of a web application. The number of roles, the number of states a record can be in, and the number of systems it has to exchange data with tell you most of it.
Where a page is rendered decides what it costs.
React can render on a server at request time, at build time, or in the browser. The Next.js App Router lets you choose per route, and that choice sets your first-load speed, whether search engines see your content, how well the page caches, and what you pay to serve it.
The four options, and when each is right
- Static: rendered at build time, served from a CDN. Fastest and cheapest. Right for marketing pages, documentation, articles — anything identical for every visitor.
- Incremental regeneration: static, with pages re-rendered in the background on a schedule or on demand. Right for catalogue and content pages that change often but not per user.
- Server-rendered per request: HTML built on the server with the user's own data in it. Right for dashboards and anything personalised that must still arrive as real HTML.
- Client-rendered: the browser fetches and draws it. Right behind a login, on interaction-heavy screens where search visibility is irrelevant and freshness matters more than first paint.
React Server Components moved the default in a sensible direction. Data fetching and heavy dependencies stay on the server, and only the components that genuinely need interactivity ship JavaScript to the browser. The practical effect is a smaller bundle, which is the single lever that moves most performance problems.
For search the rule is blunt. If a page has to rank, it renders on the server or at build time, with its content in the initial HTML — heading, body copy, canonical URL, structured data. Content that appears only after a client-side fetch is a bet, and it is not a bet worth taking on the pages that bring you business.
Authentication, data and the back office.
Authentication
Sessions in an HTTP-only cookie for most applications; tokens when a separate mobile client or a third party needs access. Social or corporate sign-in through OAuth or SAML when your buyers work at companies that require it. Password reset, email verification, invitations and a second factor are features with real work in them, so they are scoped rather than assumed. Authorisation is the harder half: permission is checked on the server for every action, not by hiding a button.
Data
PostgreSQL by default. It handles relational data, JSON, full-text search and job queues well enough that most products never need a second database. Schema changes ship as versioned migrations that run during the deploy, so every environment gets the same structure in the same order and going back is a known move rather than an improvisation. Constraints live in the database — not null, unique, foreign keys — because application code is not the last line of defence.
The back office
Every product needs internal tooling: a way to find a customer, see their state, and fix something without a developer opening a database client. It does not have to be beautiful, and early on a thin generated admin is enough. It does have to exist, be access-controlled, and record who did what. Leaving it out saves nothing — it moves the cost onto your own team, every week, indefinitely.
Core Web Vitals, measured rather than claimed.
Three field metrics decide the score Google actually uses: Largest Contentful Paint for loading, Interaction to Next Paint for responsiveness, and Cumulative Layout Shift for visual stability. The published thresholds are 2.5 seconds, 200 milliseconds and 0.1, assessed at the 75th percentile of real visits. A lab score from a developer laptop is a debugging tool. Field data from real devices on real networks is the number that counts.
What actually breaks each one
- LCP: an unoptimised hero image, a slow server response, render-blocking CSS, or a web font that holds text back. Serve the right image size in a modern format, preload the one that matters, and let text render while the font loads.
- INP: too much JavaScript running on the main thread at the moment someone taps. The fix is usually less JavaScript rather than faster JavaScript — fewer client components, less state held high in the tree, smaller dependencies.
- CLS: images and embeds without reserved space, banners injected above content, and fonts that swap to different metrics. All of it is preventable in markup.
We set a budget at the start — a page weight and a JavaScript ceiling — and check it in the build, so a regression is caught in a pull request instead of three releases later. Speed is also a cost line: pages that cache well are cheaper to serve than pages rendered per request, and the difference appears on the hosting invoice as traffic grows.
Accessibility is part of done.
An application that cannot be operated with a keyboard excludes people who cannot use a mouse, and it is usually slightly broken for everyone else too. It is also increasingly a legal requirement rather than a preference: the European Accessibility Act has applied since June 2025 to many consumer-facing digital services sold in the EU, and the standard it leans on, EN 301 549, points at WCAG.
What we do as a matter of course
- Semantic HTML first — real buttons, real links, real headings, real lists — because it carries behaviour and meaning for free.
- Every interactive element reachable by keyboard, in a sensible order, with a visible focus style that nobody deletes for looking untidy.
- Form fields with labels, errors tied to the field they belong to and announced, and error text that says what to do next.
- Contrast checked against WCAG AA, including hover, focus and disabled states.
- Alt text that carries the information, or an empty alt when the image is decoration.
- Dialogs, menus and custom controls built on tested primitives, with focus trapping and escape handling, rather than a div with a click handler.
Automated checks catch a portion of this. The rest is a keyboard pass and a screen-reader pass over the main flows, done before release rather than after somebody complains.
Hosting, and the cost of keeping it running.
A web application is not delivered when the code is written. It has to run somewhere, stay up, and be recoverable. There are two sensible shapes, and the choice is mostly about who carries the operational work.
- A managed platform: deploys on push, a preview environment per branch, a global CDN, scaling you do not configure. Least operational burden, priced by usage, and the fastest way to run a Next.js application properly.
- Containers on a cloud or a European VPS: more setup and more of your own monitoring, but predictable pricing — and the right answer when data residency, an on-premise integration or an existing platform team requires it.
Around either sits the same list: a managed PostgreSQL with automated backups, object storage for uploads, a domain and certificates, a transactional email provider, error tracking, uptime monitoring and log retention. Individually most of these are small. Together they are the running cost of the product, and they belong in the first conversation rather than the first invoice.
Set up on every project
- Separate production and staging environments, with staging pointed at the sandbox of every third party.
- Automated backups, and a restore rehearsed at least once — an untested backup is a belief, not a backup.
- Error tracking with releases tagged, so a new exception is traced to the deploy that introduced it.
- Deploys from the main branch through CI, with a way back, because rolling back is a question of when.
The repository, the cloud accounts and the domains are yours from the first commit. Nothing important is held in an account we own.
When a CMS earns its place.
A content management system is worth its complexity when people who do not write code change content often, and it is pure overhead when they do not. The question is never whether a CMS is good. It is who edits, how often, and how much damage a bad edit can do.
A CMS is the right call when
- Marketing needs to publish without waiting for a release.
- The same content is served in several languages, or to a website and an application.
- The content has structure worth modelling — products, articles, offers, pages — and is reused in more than one place.
- Someone genuinely needs drafts, previews and a publish date.
It is the wrong call when
- The pages change a few times a year and a developer is in the code every week anyway.
- What you actually need is an application screen, and modelling it as content will only make it worse.
- Nobody has been named as the person who will do the editing. An unowned CMS becomes an empty CMS.
When it is right, we build it headless: content lives in its own service behind a typed API, and the front end stays a Next.js application that caches and revalidates. This site runs that way — every page of copy is editable without a deploy — which is also how we know where the sharp edges are: modelling content as pages instead of as data, and forgetting that preview needs its own path through the cache.
The questions that come before a contract.
- How is a web project priced?
- Two models. A fixed price against a written scope when the requirements are clear enough to fix, or a monthly rate when priorities will keep moving. The estimate follows a scoping conversation, and that conversation costs you nothing.
- How long does a build take?
- Scope decides it, so we size the work before quoting instead of after. What moves the date is mostly countable: how many kinds of user there are, how many integrations, whether the design already exists, whether payments are involved, and how fast decisions come back from your side. We plan towards a first release that is genuinely usable rather than a complete feature list, then keep shipping. Dated milestones are written down before development starts.
- What stack do you build with, and why?
- TypeScript end to end: React and Next.js in the browser, Node or Python for services, PostgreSQL for data. Pages that need to be indexed are rendered on the server rather than assembled in the browser. We choose widely used, long-supported tools so the next developer who opens the repository already knows them.
- Can you take over a codebase someone else wrote?
- Yes. We start with a read-only pass over the repository and report what is safe to build on, what needs replacing, and in which order. Changes then land incrementally behind tests, instead of as a rewrite that goes dark for months.
- Who owns the code?
- You do. IP in everything we produce transfers to you, and we commit to a repository you control from day one. There is no proprietary framework to license and no lock-in on hosting or maintenance.
- What happens after launch?
- Defects against the agreed scope are ours to fix, not a new invoice. After that you can keep us on for maintenance and new features, or take the documentation and the running deployment and continue with your own team. Both are normal outcomes.