Next SEO
You cannot trust Next.js's default rendering to make your site indexable; you must audit every route's HTML output yourself, using curl or view-source.
What I’d do first
- Audit every key route with curl or view-source to confirm server-rendered HTML is present, not an empty shell.
- Export a metadata object in every layout.tsx and page.tsx, including title, description, canonical, and optionally openGraph.
- Generate a dynamic app/sitemap.ts that lists all important URLs, especially for paginated or large content sites.
- Block only non-content URLs (cart, checkout, user dashboard) in robots.txt, and test with Google's robots testing tool.
- Add hreflang tags for multilingual sites using the alternates metadata field, and verify with the hreflang validation tool.
The path I'd take
I start by checking how the page renders. Run curl https://example.com/product/shoes and look for meaningful HTML. If I see <div id="__next"></div> with nothing inside, the page is client-side rendered and Google will see a blank. I worked with a client last year who had 10,000 product pages but only 300 were indexed because every page fetched data via useEffect. Switching to SSR via getServerSideProps fixed it in two days. After that, I add metadata. In Next.js 13+ you use the metadata export in layout.tsx and page.tsx. I always include title, description, and canonical. The canonical URL is the most overlooked field. I see sites where every pagination URL points to itself, which is fine, but many forget to set any canonical at all. For example, a site with 50 category pages had no canonical tags — Google indexed all 50 as unrelated pages, diluting the authority of the main category. I set canonical: "./" on page 1, and on page 2+ I set an absolute URL pointing to the first page if that's the correct consolidation strategy. Next, the sitemap. Next.js supports app/sitemap.ts where you dynamically generate entries. I generate one entry per product, per category, per blog post. For a site with 50,000 products, the sitemap gets split automatically into chunks. I also add structured data using the jsonLd inside the metadata object. For an e-commerce product, I include Product schema with price, availability, and reviews. This helps Google show rich results. I use [Schema](/schema/) to validate the JSON-LD after deployment. Finally, I check [hreflang tags](/hreflang-tags/) if the site has multiple languages. The alternates metadata field handles this cleanly. I always test the hreflang implementation with a dedicated validator because one wrong self-referencing tag can confuse Google.
Watch-outs
The biggest trap is client-side rendering (CSR) for important content. People use Next.js because it offers SSR, but they accidentally write a page that fetches data in a client component without a fallback. If the data fetch fails or is slow, the user sees a spinner, and Google sees an empty page. I always test with JavaScript disabled in Chrome DevTools. If the page is blank, it's a problem. Another common mistake is blocking pages with robots.txt by accident. I once saw a Disallow: / rule in a client's Next.js project because they copied a legacy robots.txt from a previous site. That blocked the entire site from Google. I now always include a [robots.txt](/robots-txt/) at https://example.com/robots.txt and test it with the robots.txt tester in Search Console. The same applies to the next.config.js where you might set headers that unintentionally block crawlers. Missing canonical tags is another frequent issue. Next.js does not generate canonical tags automatically. If you forget them on [duplicate content](/duplicate-content/) pages like parameter-driven filters, Google will pick its own version, which might be wrong. I also see metadata being set only in top-level layout files, ignoring nested routes. For example, a blog post at /blog/[slug] might inherit a generic title from the parent layout if the page file does not export its own metadata. Always export metadata in every page that needs a unique title and description. Finally, watch out for image optimisation. Next.js' Image component is great for performance, but if you omit the alt attribute, you miss an accessibility and SEO signal. I add alt text programmatically from the CMS field.
What I got wrong
I once believed that because Next.js renders on the server by default, every page would be automatically indexable. That assumption cost me. I built a blog for a client using Next.js 12 with static generation (getStaticProps). The homepage and about page worked fine. But the individual blog posts were generated via dynamic routes with getStaticPaths and fallback: true. I forgot that with fallback: true, a page that has not been pre-rendered will initially be served with no content (the fallback shell) and then populated client-side. Google crawled a few of those fallback pages and saw an empty page. The client's organic traffic dropped by 40% over a month before I noticed. I switched fallback to blocking, which waited for the server-rendered HTML before sending anything. That fixed it. Another mistake: ignoring canonical tags on paginated category pages. I had an e-commerce site with 200 category pages, each with 20 products. I used Next.js' usePagination pattern, but I never set a canonical tag. Google indexed all 200 pages, many of which had overlapping products. The search results showed ten versions of the same page. I then added a canonical pointing the sub-pages to the first page, but Google was slow to consolidate. Now I treat canonical tags as non-negotiable: I add them in every route that could generate duplicate content. I also learned that the metadata API in Next.js 13 requires the exact URL for the canonical field — relative paths work but absolute ones are safer. I use environment variables to build the full URL dynamically.
Next step
Quick answers
How do I add canonical tags in Next.js?
Export a metadata object in each layout or page file with a canonical property. For example: export const metadata = { alternates: { canonical: './' } };. Use an absolute URL to avoid confusion.
Should I use SSR or SSG for SEO?
Both work well. Use SSR if content updates often or is user-specific. Use SSG for static content like blog posts. The key is to ensure the HTML contains the content, not an empty shell. Avoid static generation with fallback: true for pages you haven't pre-rendered.
How can I check if a Next.js page is client-side rendered?
Run curl https://example.com/your-page and look for actual content in the HTML. If you see only <div id="__next"> with no inner HTML, it's likely client-side rendered. Also test with JavaScript disabled in browser DevTools.
Sources
Primary documentation is linked directly. Anything commercial is marked nofollow.
- Google Search Central: SEO Starter Guide — Covers crawlability, robots.txt, sitemaps, and metadata basics applied to Next.js.
- Next.js Documentation — Authoritative for metadata API, sitemap generation, and rendering modes (SSR, SSG, CSR).
- Google Search Central: Crawling and Indexing — Provides rules on crawl budget, URL management, and canonical tags relevant to Next.js sites.
- Google Search Central: Get Started — Primary guidance on hreflang, structured data, and duplicate content issues common in Next.js projects.
Notes from Callum Bennett.