Seoxpert.io
highBest Practices

Pages Appear to Be SPA Shells Without Server-Rendered Links

Pages render as empty SPA shells with no server-rendered links, harming crawlability and indexation.

By Seoxpert Editorial · Published

Why it matters

Search engines that don't execute JavaScript, or do so unreliably, see empty pages with no internal links. This prevents discovery of other site pages and disrupts link equity flow, severely impacting SEO and site visibility.

Impact

Unresolved, only explicitly listed URLs are indexed and most site pages remain undiscovered by search engines.

How it's detected

Automated crawlers detect fewer than 3 <a> tags in server-rendered HTML on pages with heavy JavaScript usage.

Common causes

  • Relying solely on client-side rendering for navigation links
  • No server-side rendering (SSR) or static site generation (SSG) implemented
  • Primary navigation and footer links rendered only after JavaScript hydration
  • Framework misconfiguration (e.g., Next.js without getServerSideProps/getStaticProps)
  • Lack of pre-rendering in Vite/React setups

How to fix it

Implement SSR or SSG for at least your primary navigation and footer so anchor links are present in the initial HTML. For Next.js, use app-router or pages-router with getServerSideProps or getStaticProps. In Vite/React, add a pre-render step (e.g., vite-plugin-ssr) or migrate to a framework supporting SSR. As a minimum, ensure primary navigation and footer links are server-rendered before hydration.

Code examples

Problem: Navigation rendered only client-side (React)

// Server-rendered HTML contains no <a> tags
function Nav() {
  const [links, setLinks] = useState([]);
  useEffect(() => {
    setLinks([
      { href: '/home', label: 'Home' },
      { href: '/about', label: 'About' }
    ]);
  }, []);
  return (
    <nav>
      {links.map(link => (
        <a key={link.href} href={link.href}>{link.label}</a>
      ))}
    </nav>
  );
}

Fix: Navigation rendered server-side (Next.js getStaticProps

// getStaticProps ensures links are in initial HTML
export async function getStaticProps() {
  return {
    props: {
      links: [
        { href: '/home', label: 'Home' },
        { href: '/about', label: 'About' }
      ]
    }
  };
}

function Nav({ links }) {
  return (
    <nav>
      {links.map(link => (
        <a key={link.href} href={link.href}>{link.label}</a>
      ))}
    </nav>
  );
}

FAQ

Why does my site show as an empty shell to crawlers?

If navigation and links are only rendered after JavaScript loads, crawlers that don't execute JS see an empty page.

Do all search engines execute JavaScript to find links?

No. Google does, but with delays and unreliability. Bing, DuckDuckGo, and most others do not execute JavaScript at all.

Is it enough to server-render just the navigation links?

Yes, server-rendering primary navigation and footer links ensures basic crawlability and link discovery.

How can I check if my links are server-rendered?

View your page source (not the DOM inspector) and look for <a href> tags in the initial HTML.

Found this issue on your site?

Run a scan to see if Pages Appear to Be SPA Shells Without Server-Rendered Links affects your pages.

Scan my website →