Web development
Marketing sites, web applications, dashboards, customer portals and internal tools, built on React and the Next.js App Router with rendering, search architecture and performance decided up front. Core Web Vitals are an acceptance criterion here, not a report someone runs after launch.
- Next.js App Router
- TypeScript
- Core Web Vitals
- Accessibility
BUILD
Product engineering & technology
The applications, interfaces, services and infrastructure your business actually runs on — mobile, web, backend, cloud and the automation around them, built by the same people who will later measure and advertise them.
Web work now covers two quite different products under one word. A marketing site exists to be found, read and acted on by strangers. A web application exists to let known users complete work. They share a language and almost nothing else: different rendering strategies, different caching rules, different definitions of fast. Most disappointing web projects begin by treating one of them as though it were the other, and the cost of that shows up long after launch.
We build on the Next.js App Router in TypeScript, which makes the rendering decision explicit per route rather than global. Pages that can be generated ahead of time are generated ahead of time. Pages that depend on the request are rendered on the server and streamed. Only genuinely interactive parts ship as client components. That one set of decisions governs how fast the page feels, how much JavaScript the browser has to parse, and what the hosting costs each month.
The rest of the work is unglamorous and decides whether the site survives contact with your team. Semantic structure and metadata, so crawlers can understand what a page is. A component system, so later pages are assembled from parts that already exist rather than built again from scratch. Typed boundaries between the interface and whatever serves it, so an API change surfaces as a build error rather than a blank screen on a Sunday. Accessibility settled during design instead of retrofitted after a complaint.
Why people call us about the web
Nobody commissions a website because they want a website. These are the situations that usually start the conversation.
- Good content that search engines ignore
- The pages exist, but they are assembled in the browser after load, so what the crawler receives is close to an empty shell. Underneath that there is usually no canonical tag, a sitemap nobody regenerates, several URLs serving the same content, and headings chosen for their font size rather than their level. Ranking is an architecture problem long before it is a content problem.
- Fast on the test machine, slow on phones
- Lab scores get collected on a desktop connection with a warm cache. Real visitors arrive on mid-range Android phones over patchy mobile networks, where an uncompressed hero image, a render-blocking font and a stack of marketing tags compete for one main thread. Field vitals tell a different story from the number in the pitch deck, and field vitals are what your customers actually live through.
- Every content change needs a developer
- The site was delivered as hand-built pages rather than as a content model, so adding a case study means editing markup and changing a price means hunting for it across every template that repeats it. Marketing waits in the development queue, and small experiments stop being worth requesting. The real cost is not the developer hour. It is the tests your team quietly stops running.
- The dashboard slows as data grows
- It worked in the demo, on a sample small enough to hold in memory. Now it pulls the whole table into the browser, filters and sorts in JavaScript, and re-renders everything when one cell changes. The database has no index for the query those filters generate. Nothing is broken exactly. The tool has simply become something staff avoid opening unless they have to.
What we build
A given engagement uses some of this and not all of it. The scope is agreed in writing before anyone opens an editor.
Marketing sites and SEO architecture
Sites built to be crawled, indexed and understood: server-rendered content, an honest heading structure, one canonical URL per thing, and metadata generated from the content model instead of typed twice. Search visibility is designed at the routing layer, where it is cheap, rather than bolted on afterwards.
- Semantic HTML with a heading order that matches the document
- Per-route metadata, Open Graph and canonical tags generated from content
- XML sitemaps and robots rules that regenerate with every build
- Structured data for organisation, breadcrumb, article, product or FAQ where it applies
- Redirect maps that preserve old URLs through a migration
- Internal linking and pagination a crawler can actually follow
Web applications and customer portals
Signed-in surfaces where people do work: authentication, roles and permissions, forms that survive a dropped connection, and state that behaves predictably across tabs and refreshes. The loading, empty and failed states are designed with the same care as the state where everything works.
- Authentication, session handling and role-based access control
- Form handling with validation shared between client and server
- Optimistic updates with a real rollback path when the write fails
- Route-level loading, empty, error and permission-denied states
- File upload, export and progress feedback on long-running jobs
- Organisation-level data separation where the product is multi-tenant
Dashboards and internal tools
Operational software for the people who run the business: tables that stay usable at real row counts, filters that push work to the database rather than the browser, and views scoped to what each role should see. Internal tools deserve engineering, because that is where staff hours are actually spent.
- Server-side pagination, sorting and filtering against indexed queries
- Saved views, keyboard navigation and bulk actions
- Charts and summaries reading from the same source as the tables
- Spreadsheet and CSV export that does not freeze the browser
- Audit trails on destructive or financial actions
- Role-scoped visibility, so support staff see less than administrators
Rendering strategy and Core Web Vitals
Rendering is chosen per route according to how the data behaves: prerendered, revalidated on a schedule, rendered per request, streamed, or left to the client. Performance is then held in place by budgets in continuous integration and by field measurement after release.
- Static generation, incremental revalidation and per-request rendering, route by route
- Streaming with Suspense boundaries drawn around the slow data
- Image pipeline: correct sizes, modern formats, priority on the LCP element
- Font subsetting, self-hosting and metric-matched fallbacks
- JavaScript payload budgets enforced in the build pipeline
- Cache-control and revalidation headers set deliberately, including at the CDN
Design systems and accessibility
A token set and component library, so the interface holds together as it grows and a new page becomes a composition rather than a rebuild. Accessibility lives inside the component: focus order, contrast, target size and keyboard paths decided once and then inherited everywhere.
- Design tokens for colour, type scale, spacing and radius
- Components covering default, hover, focus, disabled, loading and error states
- Keyboard operability and visible focus on every interactive element
- Contrast and target-size decisions checked against WCAG criteria
- Reduced-motion and dark-mode behaviour defined at the token layer
- Component documentation your developers can read without asking us
Typed boundaries and integrations
The interface talks to services through typed contracts, and incoming data is validated at the boundary rather than trusted. Third-party embeds, payment flows, chat widgets and tag managers are added deliberately, with a stated view of what each one costs in main-thread time and in failure modes.
- Typed API clients written or generated against an agreed contract
- Schema validation on every external response and form submission
- Error boundaries that degrade one section instead of the whole page
- Payment, CRM and email integrations with retry and idempotency handling
- Tag manager and consent handling that does not block first render
- Progressive enhancement, so core paths still work when a script fails
How we work through it
The order matters more than the tooling. This is the sequence we follow, and the reason each step sits where it does.
- 01
Decide which product this actually is
Before any layout work, we establish whether the thing being built is a marketing surface, an application, an internal tool, or several of those living under one domain. That answer sets rendering, caching, authentication and the measurement plan. Getting it wrong at this point is the single most expensive mistake to undo later.
- 02
Model the content and data before pixels
We write down the entities: what a case study is, what a plan is, what a report row contains, and which of them change constantly rather than almost never. Templates fall out of that model, and so do URLs. URLs are the one part of a site you cannot casually change once people and search engines depend on them.
- 03
Choose the rendering strategy route by route
Each route gets an explicit decision: prerender, revalidate on a schedule, render per request, or hydrate on the client. We record why, because the next person to touch that page needs to know whether it is dynamic for a reason or by accident. Caching and revalidation rules are written at the same moment.
- 04
Build the system, then the pages
Tokens, primitives and layout components come first, and screens are composed from them. It looks like the slower route at the start, and stops looking that way once whole screens are composed from parts that already exist. It also means a design change lands as a token edit rather than as a search-and-replace across a folder of templates nobody has read since launch.
- 05
Ship with budgets, then measure in the field
The release goes out with performance budgets in the pipeline, error reporting connected, and analytics events implemented alongside the features that emit them. Afterwards we read real-user vitals segmented by route and device class, and fix what that field data points at rather than what a lab score flatters.
A marketing site and a web application are different products
They are commissioned in the same sentence and engineered under opposite constraints. Deciding which one you are paying for changes almost every technical choice that follows.
Marketing site
- Success is indexed pages, qualified enquiries and content that gets read
- Content changes often while the code changes rarely
- Most routes can be rendered ahead of time and cached at the edge
- The first visit is usually the only visit, so first paint decides everything
- The failure mode is invisibility: not crawled, not indexed, not found
Web application
- Success is tasks completed by signed-in users who came back on purpose
- Data changes constantly, so caching becomes a correctness question
- Most routes are personalised and render per request or on the client
- Repeat interactions matter more than the first paint ever will
- The failure mode is friction: slow inputs, lost state, silent errors
What we are usually asked to build
Three families of work, often for the same client, frequently in the same codebase and deployment.
Public web
- Corporate and product marketing sites
- Campaign landing pages with clean measurement
- Blogs, documentation and resource libraries
- Pricing, careers and policy pages built from a content model
- Multi-region or multi-language routing
Product surfaces
- Signed-in customer dashboards
- Onboarding, account and profile flows
- Billing, subscription and invoice screens
- Customer portals for orders, tickets or documents
- A web companion to an existing mobile app
Internal tools
- Operations consoles for daily back-office work
- Catalogue, content and pricing administration
- Approval, moderation and review queues
- Reporting views built on the warehouse rather than screenshots
- Bulk import, export and data-correction utilities
What performance work actually consists of
Core Web Vitals do not improve because a report was purchased. They improve when specific things are done to specific templates, and then defended on every release afterwards.
- Identify the real LCP element per template, then serve it as a correctly sized, preloaded, modern-format image instead of a hero video or a client-rendered card
- Self-host and subset fonts, with font-display and a metric-matched fallback, so text is readable immediately and does not reflow when the real face arrives
- Keep the client bundle honest: server components by default, dynamic imports below the fold, and a payload budget that fails the build when it grows
- Stream the shell and suspend the slow parts, so a page waiting on a third-party API still paints navigation, headings and layout straight away
- Set cache-control and revalidation per route on purpose, rather than letting every page quietly fall back to dynamic rendering because one import was careless
- Give third-party scripts a budget and a loading strategy: tags that block the main thread get deferred, sandboxed, or declined with the reason written down
- Reserve space for anything that arrives late, including images, embeds, consent banners and injected offers, so layout shift is designed out instead of measured after
- Break up heavy event handlers and hydration work so input latency stays low, chunking long tasks and moving debounced work off the interaction path
- Read field data rather than lab scores alone, segmented by route and device class, because the median mobile visitor is not the machine the site was built on
- Re-check the budget on every release, since regressions arrive with new features and new marketing tags, not with the original build
Technology we work with
These names are descriptive references to the software and platforms we use. They imply no partnership, sponsorship, certification, authorisation or endorsement of any kind, and each mark belongs to its respective owner.
- Application
- Next.js (App Router)
- React
- TypeScript
- Tailwind CSS
- Node.js
- Zod
- TanStack Query
- React Hook Form
- Content and data
- PostgreSQL
- Prisma
- MDX
- Headless CMS APIs
- REST and JSON APIs
- GraphQL where it already exists
- Redis
- Delivery
- Vercel
- Cloudflare
- Google Cloud
- GitHub Actions
- Docker
- Git
- Quality and measurement
- Lighthouse CI
- Playwright
- axe accessibility tooling
- Sentry
- Google Analytics 4
- Google Search Console
- Chrome UX Report
What you are left holding
- A site search engines can crawl, render and understand without workarounds
- Pages built and budgeted for mid-range phones and ordinary mobile networks
- A component system your team can extend without rebuilding templates
- Content your marketing people can change without joining a developer queue
- Repository, deployment and domain access held in your own accounts
What people ask before starting
01Can you improve the site we already have instead of rebuilding it?
Often yes, and it is usually the cheaper answer. If the content model is sound and the platform is not fighting you, most of the available gain sits in rendering, images, fonts, third-party scripts and URL structure. We will say so when a rebuild is not justified. A rebuild makes sense when the current platform blocks the thing you need next, not because the site is old.
02What actually drives the cost of a web project up or down?
The number of distinct templates, not the number of pages. Authentication and roles. Anything touching payments, invoicing or compliance. Integrations with systems we do not control, where the unknown is their API rather than our code. Content migration from an old site is routinely underestimated. Design handed over as a finished system lowers the cost; design decided during the build raises it.
03Who owns the code, the repository and the hosting?
You do. The code lives in a repository you own, with its history intact. Hosting, domain, DNS and analytics are set up in your accounts with us added as collaborators, so removing us is an access change rather than a migration. We will hand over documentation and credentials whether the engagement ends well or badly. Nothing important sits in an account you cannot log into.
04Will you build it in WordPress or a page builder if we prefer?
For a small brochure site with no application behaviour, a hosted platform is often the honest recommendation, and we will tell you that rather than sell a build you do not need. What we will not deliver is a plugin stack that becomes a security and performance liability someone pays to maintain indefinitely. If that is the brief, we are the wrong choice for it.
05Can you promise better rankings or more conversions after launch?
No, and we would treat anyone who does with suspicion. What we can commit to are the technical conditions: crawlable structure, correct canonicals and sitemaps, a performance budget defended on every release, accessible markup and measurement that works. Rankings and conversion also depend on your content, your competitors and ranking systems nobody outside those companies controls. We will show what changed technically and what the data says afterwards.
06Who keeps it fast once our marketing team starts adding tags?
That is the usual regression, and it needs a rule rather than goodwill. We put a performance budget in the pipeline so a build that exceeds it fails, and we agree a loading strategy for tags: deferred, sandboxed, or declined. Someone still has to own the decision when another tracking script is requested. We make the cost visible, and the call stays yours.
What this work usually connects to
Tell us what the site must do
Send the URL if one exists, or a description of the pages and the people who will use them. We will come back with what we would build, what we would not, and why.
Directcontact@mnfinfotech.com