Server-side plugin APIs for HTML generation, SEO optimization, and rendering customization through the gatsby-ssr.js file.
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;
}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}' });
`,
}}
/>,
]);
}
};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();
}
};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();
}
};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" />,
]);
};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;
}