How I Build Fast, SEO-Friendly Websites with Next.js
Most performance advice for Next.js stops at "use the Image component". That helps, but it is not where the wins are. After building a dozen client sites, the things that consistently matter are structural, and you make them on day one.
Render on the server unless you have a reason not to
The App Router defaults to server components, and that default is right far more often than people assume. A component only needs to be a client component if it holds state, listens to events, or touches a browser API. Everything else should stay on the server, where it costs the visitor nothing to run.
The practical test I use: if removing "use client" breaks it, it belongs on the client. If it does not, move it back.
Metadata is not an afterthought
Every route should export metadata, and every dynamic route should implement generateMetadata. It takes two minutes and it is the difference between a page that ranks and a page that does not.
- A title that describes the page, not the site
- A description written for a human, not a keyword counter
- A canonical URL, absolute
- Open Graph tags, because social previews drive real clicks
Content belongs in files, not a database
For a portfolio, a blog, or a marketing site, keeping content as markdown in the repository is faster to build, faster to serve, and free to host. Posts become static at build time. There is no query, no cold start, no CMS to log into.
The tradeoff is real - a non-technical editor cannot publish without a deploy - but for a site you maintain yourself it is the right call.
Measure the thing your users feel
Lighthouse scores are a proxy. Largest Contentful Paint on a mid-range phone over a real network is the thing. Test on the slowest device you can find, not the laptop you built it on.
What I would tell my past self
Ship the structure early. Fonts, layout, metadata and routing are cheap to get right at the start and expensive to retrofit. Everything else - animations, clever transitions, the third refactor of your button component - can wait.