A portfolio can have a lively interface without making the browser assemble every paragraph. The useful distinction is between content a visitor came to read and interactions that enhance how they explore it. Treat those as separate responsibilities.
For a content-led site, the first response should already contain the name, work, article text, and meaningful links. Animation can arrive afterwards. This gives visitors a useful page sooner and makes the content accessible to clients that do not execute the full JavaScript application.
Keep the client boundary small
In the App Router, pages and layouts are Server Components by default. Use a Client Component for state, event handlers, and browser APIs. Adding use client creates a client module boundary: its imports become part of that client graph. Put the directive near the interactive feature instead of at the top of a content-heavy page.
A Server Component can render the complete article and include a small interactive widget beside it. Client Components can also receive server-rendered content through props such as children. Client Components are normally prerendered on the first load too; the distinction is not simply HTML versus no HTML.
import { InteractiveScene } from "@/components/interactive-scene";
import { Projects } from "@/components/portfolio-sections";
export default function Page() {
return (
<main>
<h1>Muhammad Talha</h1>
<p>Senior full-stack software engineer.</p>
<InteractiveScene />
<Projects />
</main>
);
}Reference: Next.js: Server and Client Components ↗
Give each URL its own metadata
Treat a page's URL, title, description, and social metadata as one contract. A blog article needs its own canonical URL, not the portfolio homepage's canonical inherited by accident. Use a shared origin configuration and derive route-specific paths from the article slug.
Export a static metadata object for fixed content, or use generateMetadata for route-dependent content. Metadata exports belong in Server Components. A small article example can derive its title and canonical together; add the same article URL to Open Graph and the sitemap.
return {
title: post.title,
description: post.description,
alternates: {
canonical: new URL(
"/blog/" + post.slug + "/",
site.url,
).toString(),
},
};Reference: Next.js: generateMetadata ↗
Choose rendering around the content
A static export is a good fit when the complete set of pages is known at build time and the site does not need request-time server features. With output: 'export', Next.js runs Server Components during the build and emits files for a static host. Dynamic article routes need generateStaticParams so the build knows which slugs to render.
That choice has tradeoffs. Publishing a new article requires another build. Request-specific cookies, Server Actions, and runtime-only routes need a different deployment model. Pick the model that matches the product rather than assuming that one rendering strategy is best for every site.
Reference: Next.js: static exports ↗
Let animation enhance a stable page
Reserve space for a canvas or illustration before it loads. Otherwise a successful animation can still produce a distracting layout shift. Keep readable text outside the canvas, and provide a meaningful fallback when graphics support is unavailable.
Defer the renderer module, respect reduced-motion preferences, and pause work when the scene is not visible. Clean up animation frames, observers, event listeners, and GPU resources when the component unmounts. These are implementation choices that make a decorative feature easier to live with on mobile devices.
Apply the same discipline to fonts and code blocks. Load only the font families and character sets the site needs. Allow long code examples to scroll within their container so a single line does not widen the entire article on a narrow screen.
Verify what the deployed site actually serves
Review the production output, not just the development view. Confirm that article text and links exist in the returned HTML, each page has one primary heading and a unique canonical, and unknown URLs receive a real 404 response. A static host that rewrites every missing URL to the homepage can hide routing errors.
Check that the sitemap contains the intended public pages and that production metadata permits indexing. Keep staging or private builds out of the public index. After deployment, verify the real domain and inspect indexing and field performance in the appropriate search and performance tools.