or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

browser-apis.mdcomponents-hooks.mdconfiguration.mdgraphql.mdindex.mdnode-apis.mdssr-apis.md
tile.json

ssr-apis.mddocs/

Server-Side Rendering APIs

Server-side plugin APIs for HTML generation, SEO optimization, and rendering customization through the gatsby-ssr.js file.

Capabilities

GatsbySSR Interface

Main interface for server-side rendering plugin APIs that control HTML generation, meta tags, and rendering customization.

/**
 * Main interface for gatsby-ssr.js plugin APIs
 */
interface GatsbySSR {
  /** Before HTML render */
  onPreRenderHTML?: (args: PreRenderHTMLArgs, pluginOptions?: PluginOptions) => void;
  /** Render body content */
  onRenderBody?: (args: RenderBodyArgs, pluginOptions?: PluginOptions) => void;
  /** Replace server renderer */
  replaceRenderer?: (args: ReplaceRendererArgs, pluginOptions?: PluginOptions) => void;
  /** Wrap page element */
  wrapPageElement?: (args: WrapPageElementNodeArgs, pluginOptions?: PluginOptions) => React.ReactElement;
  /** Wrap root element */
  wrapRootElement?: (args: WrapRootElementNodeArgs, pluginOptions?: PluginOptions) => React.ReactElement;
}

HTML Generation APIs

Control HTML document structure and content during server-side rendering.

/**
 * Pre-render HTML arguments
 */
interface PreRenderHTMLArgs {
  getHeadComponents: () => React.ReactElement[];
  replaceHeadComponents: (components: React.ReactElement[]) => void;
  getPreBodyComponents: () => React.ReactElement[];
  replacePreBodyComponents: (components: React.ReactElement[]) => void;
  getPostBodyComponents: () => React.ReactElement[];
  replacePostBodyComponents: (components: React.ReactElement[]) => void;
  pathname: string;
}

/**
 * Render body arguments
 */
interface RenderBodyArgs {
  setHeadComponents: (components: React.ReactElement[]) => void;
  setHtmlAttributes: (attributes: HtmlAttributes) => void;
  setBodyAttributes: (attributes: BodyAttributes) => void;
  setPreBodyComponents: (components: React.ReactElement[]) => void;
  setPostBodyComponents: (components: React.ReactElement[]) => void;
  setBodyProps: (props: object) => void;
  pathname: string;
}

interface HtmlAttributes {
  [key: string]: string | number | boolean;
}

interface BodyAttributes {
  [key: string]: string | number | boolean;
}

/**
 * Called before HTML rendering - modify components here
 */
onPreRenderHTML?: (args: PreRenderHTMLArgs, pluginOptions?: PluginOptions) => void;

/**
 * Called during HTML rendering - add components and attributes
 */
onRenderBody?: (args: RenderBodyArgs, pluginOptions?: PluginOptions) => void;

Usage Examples:

// gatsby-ssr.js - HTML generation
import React from "react";

exports.onRenderBody = ({
  setHtmlAttributes,
  setBodyAttributes,
  setHeadComponents,
  setPreBodyComponents,
  setPostBodyComponents,
  pathname,
}) => {
  // Set HTML lang attribute
  setHtmlAttributes({ lang: "en" });
  
  // Set body class based on page
  const bodyClass = pathname === "/" ? "home-page" : "inner-page";
  setBodyAttributes({ className: bodyClass });
  
  // Add meta tags and external resources
  setHeadComponents([
    <meta key="viewport" name="viewport" content="width=device-width, initial-scale=1" />,
    <meta key="theme-color" name="theme-color" content="#663399" />,
    <link key="preconnect-google" rel="preconnect" href="https://fonts.googleapis.com" />,
    <link
      key="google-fonts"
      rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
    />,
  ]);
  
  // Add components before body content
  setPreBodyComponents([
    <div key="skip-nav" id="skip-nav">
      <a href="#main-content">Skip to main content</a>
    </div>,
  ]);
  
  // Add components after body content
  setPostBodyComponents([
    <script
      key="theme-script"
      dangerouslySetInnerHTML={{
        __html: `
          (function() {
            const theme = localStorage.getItem('theme') || 'light';
            document.documentElement.setAttribute('data-theme', theme);
          })();
        `,
      }}
    />,
  ]);
};

exports.onPreRenderHTML = ({
  getHeadComponents,
  replaceHeadComponents,
  getPostBodyComponents,
  replacePostBodyComponents,
  pathname,
}) => {
  // Remove unused CSS for production builds
  if (process.env.NODE_ENV === "production") {
    const headComponents = getHeadComponents();
    const filteredHeadComponents = headComponents.filter((component) => {
      // Remove unused stylesheets
      if (component.type === "style" && component.props.dangerouslySetInnerHTML) {
        const css = component.props.dangerouslySetInnerHTML.__html;
        return !css.includes(".unused-class");
      }
      return true;
    });
    replaceHeadComponents(filteredHeadComponents);
  }
  
  // Add analytics script for specific pages
  if (pathname.startsWith("/blog/")) {
    const postBodyComponents = getPostBodyComponents();
    replacePostBodyComponents([
      ...postBodyComponents,
      <script
        key="blog-analytics"
        dangerouslySetInnerHTML={{
          __html: `
            // Blog-specific analytics
            gtag('event', 'page_view', { page_path: '${pathname}' });
          `,
        }}
      />,
    ]);
  }
};

Element Wrapping APIs

Wrap page and root elements with custom server-side components.

/**
 * Wrap page element arguments (SSR)
 */
interface WrapPageElementNodeArgs {
  element: React.ReactElement;
  props: PageProps;
  pathname: string;
}

/**
 * Wrap root element arguments (SSR)  
 */
interface WrapRootElementNodeArgs {
  element: React.ReactElement;
  pathname: string;
}

/**
 * Wrap each page with custom elements during SSR
 */
wrapPageElement?: (
  args: WrapPageElementNodeArgs,
  pluginOptions?: PluginOptions
) => React.ReactElement;

/**
 * Wrap the root element during SSR
 */
wrapRootElement?: (
  args: WrapRootElementNodeArgs,
  pluginOptions?: PluginOptions
) => React.ReactElement;

Usage Examples:

// gatsby-ssr.js - Element wrapping
import React from "react";
import { Provider } from "react-redux";
import { ServerStyleSheet, StyleSheetManager } from "styled-components";
import { HelmetProvider } from "react-helmet-async";
import Layout from "./src/components/Layout";
import { createStore } from "./src/store";

// Wrap root with global providers for SSR
exports.wrapRootElement = ({ element, pathname }) => {
  // Create server-side store
  const store = createStore();
  
  // Helmet context for SSR
  const helmetContext = {};
  
  return (
    <Provider store={store}>
      <HelmetProvider context={helmetContext}>
        {element}
      </HelmetProvider>
    </Provider>
  );
};

// Wrap each page with layout for SSR
exports.wrapPageElement = ({ element, props, pathname }) => {
  return (
    <Layout {...props} pathname={pathname}>
      {element}
    </Layout>
  );
};

// Handle styled-components SSR
exports.replaceRenderer = ({ bodyComponent, replaceBodyHTMLString, setHeadComponents }) => {
  const sheet = new ServerStyleSheet();
  
  try {
    const bodyHTML = ReactDOMServer.renderToString(
      sheet.collectStyles(bodyComponent)
    );
    
    replaceBodyHTMLString(bodyHTML);
    setHeadComponents([sheet.getStyleElement()]);
  } catch (error) {
    console.error("Error during SSR:", error);
  } finally {
    sheet.seal();
  }
};

Custom Renderer APIs

Replace the default server-side renderer with custom rendering logic.

/**
 * Replace renderer arguments
 */
interface ReplaceRendererArgs {
  bodyComponent: React.ReactElement;
  replaceBodyHTMLString: (html: string) => void;
  setHeadComponents: (components: React.ReactElement[]) => void;
  pathname: string;
}

/**
 * Replace the server-side renderer
 */
replaceRenderer?: (
  args: ReplaceRendererArgs,
  pluginOptions?: PluginOptions
) => void;

Usage Examples:

// gatsby-ssr.js - Custom renderer
import React from "react";
import ReactDOMServer from "react-dom/server";
import { ServerStyleSheet } from "styled-components";
import { ChunkExtractor } from "@loadable/server";

exports.replaceRenderer = ({
  bodyComponent,
  replaceBodyHTMLString,
  setHeadComponents,
  pathname,
}) => {
  // Handle styled-components
  const sheet = new ServerStyleSheet();
  
  // Handle loadable-components
  const extractor = new ChunkExtractor({
    statsFile: path.resolve("./public/loadable-stats.json"),
    entrypoints: ["app"],
  });
  
  try {
    // Wrap with providers
    const styledBody = sheet.collectStyles(bodyComponent);
    const loadableBody = extractor.collectChunks(styledBody);
    
    // Render to string
    const bodyHTML = ReactDOMServer.renderToString(loadableBody);
    
    // Replace body HTML
    replaceBodyHTMLString(bodyHTML);
    
    // Set head components
    const styleTags = sheet.getStyleElement();
    const linkTags = extractor.getLinkElements();
    const scriptTags = extractor.getScriptElements();
    
    setHeadComponents([...styleTags, ...linkTags, ...scriptTags]);
  } catch (error) {
    console.error("Custom renderer error:", error);
    // Fallback to default rendering
    const bodyHTML = ReactDOMServer.renderToString(bodyComponent);
    replaceBodyHTMLString(bodyHTML);
  } finally {
    sheet.seal();
  }
};

SEO and Meta Tags Management

Comprehensive SEO optimization through HTML manipulation.

Usage Examples:

// gatsby-ssr.js - SEO optimization
import React from "react";

exports.onRenderBody = ({ setHeadComponents, pathname }) => {
  // Dynamic meta tags based on page
  const getPageMeta = (pathname) => {
    const meta = {
      "/": {
        title: "Home - My Gatsby Site",
        description: "Welcome to my blazing fast Gatsby site",
        image: "/images/home-og.jpg",
      },
      "/about": {
        title: "About - My Gatsby Site", 
        description: "Learn more about our company and mission",
        image: "/images/about-og.jpg",
      },
    };
    
    return meta[pathname] || {
      title: "My Gatsby Site",
      description: "A blazing fast static site built with Gatsby",
      image: "/images/default-og.jpg",
    };
  };
  
  const pageMeta = getPageMeta(pathname);
  const siteUrl = "https://mysite.com";
  
  setHeadComponents([
    // Basic meta tags
    <title key="title">{pageMeta.title}</title>,
    <meta key="description" name="description" content={pageMeta.description} />,
    
    // Open Graph
    <meta key="og:type" property="og:type" content="website" />,
    <meta key="og:title" property="og:title" content={pageMeta.title} />,
    <meta key="og:description" property="og:description" content={pageMeta.description} />,
    <meta key="og:image" property="og:image" content={`${siteUrl}${pageMeta.image}`} />,
    <meta key="og:url" property="og:url" content={`${siteUrl}${pathname}`} />,
    
    // Twitter Card
    <meta key="twitter:card" name="twitter:card" content="summary_large_image" />,
    <meta key="twitter:title" name="twitter:title" content={pageMeta.title} />,
    <meta key="twitter:description" name="twitter:description" content={pageMeta.description} />,
    <meta key="twitter:image" name="twitter:image" content={`${siteUrl}${pageMeta.image}`} />,
    
    // Additional SEO
    <meta key="robots" name="robots" content="index,follow" />,
    <meta key="googlebot" name="googlebot" content="index,follow" />,
    <link key="canonical" rel="canonical" href={`${siteUrl}${pathname}`} />,
    
    // Performance hints
    <link key="dns-prefetch-google" rel="dns-prefetch" href="//fonts.googleapis.com" />,
    <link key="preconnect-google" rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />,
  ]);
};

Types

Core SSR Types

interface PageProps<
  DataType = object,
  PageContextType = object,
  LocationState = WindowLocation["state"],
  ServerDataType = object
> {
  data: DataType;
  location: WindowLocation<LocationState>;
  navigate: NavigateFn;
  pageContext: PageContextType;
  params: Record<string, string>;
  path: string;
  uri: string;
  pageResources: PageResources;
  serverData?: ServerDataType;
}

type NavigateFn = (
  to: string,
  options?: {
    replace?: boolean;
    state?: any;
  }
) => Promise<void>;

interface WindowLocation<S = unknown> {
  pathname: string;
  search: string;
  hash: string;
  href: string;
  origin: string;
  protocol: string;
  host: string;
  hostname: string;
  port: string;
  state: S;
  key?: string;
}