Dynamic rendering as a workaround

On some websites, JavaScript generates additional content on a page when it's executed in the browser. This is called client-side rendering. While Google Search executes JavaScript, there are JavaScript features with limitations in Google Search and some pages may encounter problems with content not showing up in the rendered HTML. Other search engines may choose to ignore JavaScript and won't see JavaScript-generated content.

Dynamic rendering is a workaround for websites where JavaScript-generated content is not available to search engines. A dynamic rendering server detects bots that may have problems with JavaScript-generated content and serves a server-rendered version without JavaScript to these bots while showing the client-side rendered version of the content to users.

Dynamic rendering is a workaround and not a recommended solution, because it creates additional complexities and resource requirements.

Sites that should use dynamic rendering

Dynamic rendering is a workaround for indexable, public JavaScript-generated content that changes rapidly, or content that uses JavaScript features that aren't supported by the crawlers you care about. Not all sites need to use dynamic rendering, and there are better solutions than dynamic rendering as explained in this article on rendering on the web.

Understand how dynamic rendering works

Dynamic rendering requires your web server to detect crawlers (for example, by checking the user agent). Requests from crawlers are routed to a renderer, requests from users are served normally. Where needed, the dynamic renderer serves a version of the content that's suitable to the crawler, for example, it may serve a static HTML version. You can choose to enable the dynamic renderer for all pages or on a per-page basis.

A diagram that shows how dynamic rendering works. The diagram shows the server serving
              initial HTML and JavaScript content directly to the browser. In contrast, the diagram
              shows the server serving initial HTML and JavaScript to a renderer, which converts the
              initial HTML and JavaScript to static HTML. Once the content is converted, the
              renderer serves static HTML to the crawler.

Dynamic rendering is not cloaking

Googlebot generally doesn't consider dynamic rendering as cloaking. As long as your dynamic rendering produces similar content, Googlebot won't view dynamic rendering as cloaking.

When you're setting up dynamic rendering, your site may produce error pages. Googlebot doesn't consider these error pages as cloaking and treats the error as any other error page.

Using dynamic rendering to serve completely different content to users and crawlers can be considered cloaking. For example, a website that serves a page about cats to users and a page about dogs to crawlers can be considered cloaking.

Implement dynamic rendering

To setup dynamic rendering for your content, follow our general guidelines. You will need to refer to your specific configuration details, as they vary greatly between implementations.

  1. Install and configure a dynamic renderer (for example, Puppeteer, Rendertron, or prerender.io) to transform your content into static HTML that's easier for crawlers to consume.
  2. Choose the user agents that you want to receive your static HTML and refer to your specific configuration details on how to update or add user agents. Here's an example of a list of common user agents in the Rendertron middleware:
    export const botUserAgents = [
      'googlebot',
      'bingbot',
      'linkedinbot',
      'mediapartners-google',
    ];
  3. If pre-rendering slows down your server or you see a high number of pre-rendering requests, consider implementing a cache for pre-rendered content, or verifying that requests are from legitimate crawlers.
  4. Determine if the user agents require desktop or mobile content. Use dynamic serving to provide the appropriate desktop or mobile version. Here's an example of how a configuration could determine if a user agent requires desktop or mobile content:
    isPrerenderedUA = userAgent.matches(botUserAgents)
    isMobileUA = userAgent.matches(['mobile', 'android'])
    
    if (!isPrerenderedUA) { } else { servePreRendered(isMobileUA) }
  5. Configure your server to deliver the static HTML to the crawlers that you selected. There are several ways you can do this depending on your technology; here are a few examples:
    • Proxy requests coming from crawlers to the dynamic renderer.
    • Pre-render as part of your deployment process and make your server serve the static HTML to crawlers.
    • Build dynamic rendering into your custom server code.
    • Serve static content from a pre-rendering service to crawlers.
    • Use a middleware for your server (for example, the rendertron middleware).

Verify your configuration

After you finish implementing dynamic rendering, verify that everything works as expected by checking a URL with the following tests:

  1. Test your mobile and desktop content with the URL Inspection tool to make sure that the desktop and mobile content is also visible on the rendered page (the rendered page is how Google sees your page).

    Success: The desktop and mobile content matches what you expect a user to see.

    Try again: If the content you see doesn't match what you expect, see the troubleshooting section.

  2. If you use structured data, test that your structured data renders properly with the Rich Results Test.

    Success: The structured data appears as you expect it to.

    Try again: If the structured data doesn't appear as you expect it to, see the troubleshooting section.

Troubleshooting

If your content is showing errors in the URL Inspection tool or if it isn't appearing in Google Search results, try to resolve the most common issues. If you're still experiencing issues, post a new topic in the Google Search Central Help Community.

Content is incomplete or looks different

What caused the issue: Your renderer might be misconfigured or your web application might be incompatible with your rendering solution. Sometimes timeouts can also cause content to not be rendered correctly.

Fix the issue: Refer to the documentation for your specific rendering solution to debug your dynamic rendering setup.

High response times

What caused the issue: Using a headless browser to render pages on demand often causes high response times, which can cause crawlers to cancel the request and not index your content. High response times can also result in crawlers reducing their crawl-rate when crawling and indexing your content.

Fix the issue

  1. Set up a cache for the pre-rendered HTML or create a static HTML version of your content as part of your build process.
  2. Make sure to enable the cache in your configuration (for example, by pointing crawlers to your cache).
  3. Check that crawlers get your content quickly by using testing tools such as the Rich Results Test or webpagetest (with a custom user agent string from the list of Google Crawler user agents). Your requests should not time out.

Web Components don't render as expected

What caused the issue: Shadow DOM is isolated from the rest of the page. Rendering solutions (like Rendertron) cannot see content inside the isolated shadow DOM. For more information see the best practices for web components.

Fix the issue

  1. Load the webcomponents.js polyfills for custom elements and shadow DOM.
  2. Use the Rich Results Test or the URL Inspection Tool to check if the content appears in the rendered HTML of your rendering solution.

Structured data is missing

What caused the issue: Missing the structured data user agent, or not including JSON-LD script tags in the output can cause structured data errors.

Fix the issue

  1. Use the Rich Results Test to make sure the structured data is present on the page. Then configure the user agent to test the pre-rendered content with the desktop or mobile Googlebot.
  2. Make sure JSON-LD script tags are included in the dynamically rendered HTML of your content. Consult the documentation of your rendering solution for more information.