High-performance third-party embeds, Google Tag Manager, analytics, and non-blocking script strategies for Rspress sites.
π Read the Documentation Β· π View Interactive Demo Β· π¦ NPM Package
rspress-plugin-third-parties brings Next.js-style third-party optimizations (@next/third-parties) directly to Rspress.
External embeds (like YouTube, Google Maps, and Twitter/X posts) and script tags (like GA4 and GTM) are notorious for blocking the main thread, lowering Lighthouse performance scores, and causing layout shifts. This plugin solves that by providing:
- β‘ React Compiler Pre-Optimized: Ships fully compiled with automated auto-memoization at build-time. Zero unnecessary re-renders.
- π Non-Blocking Execution: Lazy loading scripts using
requestIdleCallbackand fine-grained loading strategies. - β‘ React 19 Resource Pre-initialization: Leverages native
ReactDOM.preinitandReactDOM.preloadduring SSG/SSR. - π§ Smart Memory Caching: Built-in script deduplication and stylesheet cache registry.
- π οΈ Zero-Config Global GA: Inject Google Analytics across all documentation pages automatically via your
rspress.config.ts. - π¦ Zero-Weight Embeds: Powered by
third-party-capitalfor lightweight HTML generation.
- Installation
- Plugin Registration
- Component Reference
- Event Tracking Utility
- Script Loading Strategies
- Under the Hood Mechanics
- License
Install via your preferred package manager:
# pnpm
pnpm add rspress-plugin-third-parties
# npm
npm install rspress-plugin-third-parties
# yarn
yarn add rspress-plugin-third-partiesTo auto-inject Google Analytics globally across your Rspress documentation, register the plugin in your rspress.config.ts:
// rspress.config.ts
import { defineConfig } from 'rspress/config';
import { pluginThirdParties } from 'rspress-plugin-third-parties/plugin';
export default defineConfig({
plugins: [
pluginThirdParties({
googleAnalytics: {
gaId: 'G-XXXXXXXXXX', // Your GA4 Measurement ID
debugMode: false, // Set to true for local testing
dataLayerName: 'dataLayer',
},
}),
],
});The foundation component powering all external integrations. It manages script mounting, deduplication, inline scripts, stylesheet preloading, and lifecycle events (onLoad, onReady, onError).
import { Script } from 'rspress-plugin-third-parties';
{/* Load after interactive */}
<Script
src="https://sanjaiyan-cool.web.app/script/v1/1/SanWebMaker.js"
strategy="afterInteractive"
onLoad={() => console.log('Widgets loaded!')}
/>
{/* Inline Script */}
<Script id="custom-inline-script">
{`console.log('Inline script running');`}
</Script>| Prop | Type | Default | Description |
|---|---|---|---|
src |
string |
"" |
Source URL of the script. |
strategy |
"afterInteractive" | "lazyOnload" | "beforeInteractive" |
"afterInteractive" |
Script loading strategy. |
id |
string |
src |
Unique script identifier used for cache deduplication. |
onLoad |
(e: Event) => void |
β | Callback fired when the script successfully loads. |
onReady |
() => void |
β | Callback fired on load, or immediately if script was previously loaded. |
onError |
(e: Event) => void |
β | Callback fired when script loading fails. |
stylesheets |
string[] |
β | Array of stylesheet URLs to pre-init/inject alongside the script. |
nonce |
string |
β | CSP nonce string. |
Powered by lite-youtube-embed under the hood. Renders an ultra-fast visual facade that defers the heavy YouTube player until play is clickedβkeeping your main thread pristine and Lighthouse scores flawless.
import { YouTubeEmbed } from 'rspress-plugin-third-parties';
# Youtube Video
<YouTubeEmbed
videoid="sSbDtQTtwBY"
height={400}
playlabel="A Lineage of Logic, A Future of Code"
/>| Prop | Type | Default | Description |
|---|---|---|---|
videoid |
string |
Required | The YouTube Video ID. |
height |
number |
null |
Container height in pixels. |
width |
number |
null |
Container width in pixels. |
playlabel |
string |
'Play' |
Accessible play button aria-label. |
params |
string |
β | Additional iframe URL parameters (e.g. "controls=0&start=10"). |
style |
string |
β | Additional CSS styles for the container. |
Provides performance-optimized embeds for Google Maps without blocking the browser during initial navigation.
import { GoogleMapsEmbed } from 'rspress-plugin-third-parties';
<GoogleMapsEmbed
apiKey="YOUR_GOOGLE_MAPS_API_KEY"
mode="place"
q="Point Pedro, Sri Lanka"
height={450}
zoom="14"
/>| Prop | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
Required | Your Google Maps Embed API key. |
mode |
"place" | "view" | "directions" | "streetview" | "search" |
Required | Embed mode. |
q |
string |
β | Map search query or location place name. |
center |
string |
β | Lat/lng center point ("9.814937,81.166080"). |
zoom |
string |
β | Map zoom level (0 to 21). |
maptype |
"roadmap" | "satellite" |
'roadmap' |
Map rendering mode. |
language |
string |
β | Map language code (e.g. 'ta', 'en'). |
region |
string |
β | Regional country code. |
loading |
"eager" | "lazy" |
'lazy' |
Native iframe loading attribute. |
Zero-overhead Twitter/X post embed powered by react-tweet. Fetches raw post data and renders lightweight, native React DOM components styled identically to X/Twitter UI without downloading heavy widgets.js scripts or layout-shifting IFrames.
import { TweetEmbed } from 'rspress-plugin-third-parties';
{/* Basic Tweet */}
<TweetEmbed id="2017178323550605790" />
{/* Tweet with optional forced theme */}
<TweetEmbed id="2017178323550605790" theme="dark" />| Prop | Type | Default | Description |
|---|---|---|---|
id |
string |
β | The Tweet / X Post ID (extracted from tweet URL). |
theme |
"light" | "dark" |
useDark() |
Optional theme override. Inherits Rspress reactive theme if omitted. |
caption |
ReactNode |
β | Optional accessible caption rendered in a <figcaption> tag beneath the tweet. |
apiUrl |
string |
β | Custom proxy API URL for fetching raw tweet data. |
fallback |
ReactNode |
β | Loading skeleton component rendered while tweet payload is being fetched. |
components |
TwitterComponents |
β | Custom UI component overrides for tweet elements (e.g. custom avatar, media, or links). |
fetchOptions |
RequestInit |
β | Custom fetch headers or request configuration sent to the tweet API. |
onError |
(error: any) => any |
β | Callback function fired if tweet data fetching or rendering fails. |
className |
string |
β | Additional CSS class names applied to the container <figure> element. |
Explicitly embed Google Analytics 4 (GA4) inside MDX pages or custom layout components.
import { GoogleAnalytics } from 'rspress-plugin-third-parties';
<GoogleAnalytics gaId="G-XXXXXXXXXX" debugMode={true} />| Prop | Type | Default | Description |
|---|---|---|---|
gaId |
string |
Required | GA4 Measurement ID (G-XXXXXXXXXX). |
dataLayerName |
string |
'dataLayer' |
Custom global dataLayer array name. |
debugMode |
boolean |
false |
Enables Google Analytics debug mode. |
nonce |
string |
β | CSP nonce string. |
Integrate Google Tag Manager (GTM) with support for custom domains, authentication, preview environments, and custom initial dataLayer states.
import { GoogleTagManager } from 'rspress-plugin-third-parties';
<GoogleTagManager
gtmId="GTM-XXXXXXX"
dataLayer={{ userRole: 'developer', env: 'production' }}
/>| Prop | Type | Default | Description |
|---|---|---|---|
gtmId |
string |
β | GTM Container ID (GTM-XXXXXXX). |
gtmScriptUrl |
string |
'https://www.googletagmanager.com/gtm.js' |
Custom domain proxy URL for GTM script. |
dataLayer |
Record<string, JSONValue> |
β | Initial dataLayer object payload. |
dataLayerName |
string |
'dataLayer' |
Custom dataLayer global array name. |
auth |
string |
β | GTM Environment authentication string (gtm_auth). |
preview |
string |
β | GTM Environment preview string (gtm_preview). |
nonce |
string |
β | CSP nonce string. |
Note
You must provide either gtmId or a custom gtmScriptUrl containing the ID parameter.
Send custom Google Analytics events dynamically from anywhere in your client-side React code:
import { sendGAEvent } from 'rspress-plugin-third-parties';
function FeedbackButton() {
const handleClick = () => {
sendGAEvent('event', 'documentation_helpful', {
page: window.location.pathname,
vote: 'yes',
});
};
return <button onClick={handleClick}>Helpful π</button>;
}| Strategy | Timing | Ideal For |
|---|---|---|
afterInteractive (Default) |
Injected immediately after page hydration. | Analytics, Tag Managers, essential widgets. |
lazyOnload |
Injected during browser idle time (requestIdleCallback). |
Chat widgets, social feeds, low-priority pixels. |
beforeInteractive |
Rendered into raw HTML prior to client JS execution during SSG. | Critical consent scripts, anti-bot security. |
βββββββββββββββββββββββββββββββββββββββββββ
β Rspress SSG Build Phase β
ββββββββββββββββββββββ¬βββββββββββββββββββββ
β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β Server Pre-render Execution β
β Calls safePreload() & safePreinit() β
ββββββββββββββββββββββ¬βββββββββββββββββββββ
β
βΌ
HTML Output with Native Preloads
β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β Hydration & Browser Idle β
ββββββββββββββββββββββ¬βββββββββββββββββββββ
β
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
[afterInteractive] [lazyOnload] [Cache Deduplication]
Appends script after Uses requestIdleCallback Prevents fetching identical
client hydration. to minimize TBT. `src`/`id` entries twice.
- React 19 Resource Pre-init: Uses
ReactDOM.preinitandReactDOM.preloadto declare external resource hints before browser parsing. - Script Caching: Maintains global
ScriptCacheandLoadCacheMap/Set singletons, ensuring identical script tags are never loaded twice across page transitions. - Graceful React 18 Fallback: Fallbacks to dynamic
document.createElement('link')stylesheet injection when React 19 resource pre-init functions are unavailable.
Run these commands from the monorepo root:
pnpm install
pnpm --filter rspress-plugin-third-parties build
pnpm exec playwright install chromium
pnpm e2e rspress-plugin-third-parties
pnpm lint
pnpm --filter rspress-plugin-third-parties docs:build
pnpm --filter rspress-plugin-third-parties docs:devThe package uses the shared Rstack toolchain, TypeScript configuration, and pnpm
catalog. It publishes ESM components and a separate Node.js plugin entry. Playwright
tests run alongside the other plugins through the root pnpm e2e command.
Versioning and publishing use the root Changesets configuration.
MIT Β© Sanjaiyan Parthipan and the Rspress Community (from September 2026 onward)