Skip to content

perf: lazy-load Algolia DocSearch to reduce initial bundle size and eliminate render-blocking CSS - #5673

Open
patilpratik1905 wants to merge 1 commit into
asyncapi:masterfrom
patilpratik1905:perf_enh/lazyLoad_AlgoliaDocSearch
Open

perf: lazy-load Algolia DocSearch to reduce initial bundle size and eliminate render-blocking CSS#5673
patilpratik1905 wants to merge 1 commit into
asyncapi:masterfrom
patilpratik1905:perf_enh/lazyLoad_AlgoliaDocSearch

Conversation

@patilpratik1905

@patilpratik1905 patilpratik1905 commented Jul 31, 2026

Copy link
Copy Markdown

Problem

Every page on the AsyncAPI website loaded Algolia DocSearch assets upfront, even though the search modal is only displayed after a user explicitly opens it.

This resulted in:

  • Render-blocking CSS via:

    @import url(https://cdn.jsdelivr.net/npm/@docsearch/css@3);

    in globals.css, delaying the browser's first paint and negatively affecting FCP and LCP.

  • Unnecessary JavaScript because DocSearchModal from @docsearch/react was statically imported, causing it to be included in the initial JavaScript bundle and increasing parsing time, TBT, and TTI.

  • Every visitor downloading and parsing these assets regardless of whether they ever used the search feature.

On Lighthouse mobile (4× CPU slowdown), this unnecessarily increased the critical rendering path and negatively impacted Core Web Vitals.

Root Cause

globals.css synchronously imported the Algolia stylesheet using @import, which is render-blocking by specification since the browser must download and parse the stylesheet before painting.

AlgoliaSearch.tsx statically imported DocSearchModal:

import { DocSearchModal } from '@docsearch/react';

This prevented webpack from code-splitting the modal, causing it to be bundled into the initial JavaScript payload despite only being needed after explicit user interaction.

Solution

Implemented a two-part optimization to defer both the JavaScript and CSS until the search modal is actually opened.

Remove Render-Blocking CSS

Removed the global stylesheet import from globals.css:

- @import url(https://cdn.jsdelivr.net/npm/@docsearch/css@3);
+ /* Algolia DocSearch CSS is loaded on demand in AlgoliaSearch.tsx */

Lazy-load DocSearchModal

Replaced the static import with a dynamic import using next/dynamic:

const DocSearchModal = dynamic(
  () =>
    import('@docsearch/react').then((mod) => ({
      default: mod.DocSearchModal,
    })),
  {
    ssr: false,
  }
);

This creates a separate chunk that is only downloaded when the search modal is first opened.

Load DocSearch CSS On Demand

Since @docsearch/css is a CSS-only package and cannot be dynamically imported as JavaScript, the stylesheet is loaded via the DOM when the modal is opened for the first time.

The stylesheet is:

  • Loaded only when needed
  • Injected exactly once
  • Cached by the browser for subsequent openings

Keep Lightweight Functionality Synchronous

The following remain eagerly loaded to preserve instant interactions:

  • AlgoliaSearch wrapper component
  • Keyboard shortcut listeners (Ctrl+K, /, Escape)
  • SearchButton component
  • Search trigger UI

This keeps keyboard shortcuts responsive while deferring the heavy modal implementation.


Testing

Manual

  • ✅ Search button opens the modal correctly
  • ✅ Keyboard shortcuts (Ctrl+K, /, Escape) work immediately
  • ✅ Search queries, navigation, and results function correctly
  • ✅ Correct Algolia index selected for docs and non-docs pages
  • ✅ Mobile navigation behavior preserved
  • ✅ Dark mode styling verified
  • ✅ DevTools Network verification:
    • Initial page load does not request DocSearch CSS
    • First modal open downloads the stylesheet
    • Subsequent openings use the browser cache

Performance Impact

Metric Before After
Initial JS bundle Included DocSearchModal Deferred into a separate chunk
Initial CSS Render-blocking @import Loaded on demand
First Contentful Paint (FCP) Delayed by external CSS Improved
Largest Contentful Paint (LCP) Delayed by CSS and JS Improved
Total Blocking Time (TBT) Modal JS parsed on initial load Deferred until modal opens
Speed Index Higher due to blocking resources Improved
First search modal open Instant Small one-time load (~100–200 ms)
Subsequent modal opens Instant Instant (browser cached)

Related Issue

Summary by CodeRabbit

  • Performance
    • Search modal resources and styles now load only when the search experience is opened, improving initial page loading.
  • Style
    • Updated stylesheet formatting and consistency without changing the visual design.
    • Added required carousel styling imports.

@netlify

netlify Bot commented Jul 31, 2026

Copy link
Copy Markdown

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 5cc7bf6
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/6a6c36dd9dbf260008c79646
😎 Deploy Preview https://deploy-preview-5673--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The search modal now loads client-side when opened, and DocSearch CSS is injected on demand. Search behavior remains unchanged. Global CSS imports and formatting were normalized.

Changes

Algolia Search

Layer / File(s) Summary
Lazy modal and stylesheet loading
components/AlgoliaSearch.tsx, styles/globals.css
The DocSearch modal uses a client-only dynamic import. Its CSS loads when the modal opens.
Search interaction preservation
components/AlgoliaSearch.tsx
Props, predicates, navigation, portals, keyboard handling, callbacks, context wiring, and button behavior were reformatted without behavior changes.
Global stylesheet updates
styles/globals.css
The global DocSearch import was removed. Swiper imports remain. CSS formatting and color casing were normalized.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AlgoliaSearch
  participant documentHead
  participant DocSearchModal
  AlgoliaSearch->>documentHead: Inject DocSearch CSS when open
  AlgoliaSearch->>DocSearchModal: Dynamically load and render while open
Loading

Suggested reviewers: akshatnema, anshgoyalevil, asyncapi-bot-eve

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes lazy-loading Algolia DocSearch to improve initial page-load performance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

styles/globals.css

Parsing error: ESLint was configured to run on <tsconfigRootDir>/styles/globals.css using parserOptions.project: /tsconfig.json
The extension for the file (.css) is non-standard. You should add parserOptions.extraFileExtensions to your config.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@asyncapi-bot

Copy link
Copy Markdown
Contributor

⚡️ Lighthouse report for the changes in this PR:

Category Score
🟠 Performance 52
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-5673--asyncapi-website.netlify.app/

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
components/AlgoliaSearch.tsx (1)

331-331: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Pin the deferred stylesheet to the tested DocSearch release.

The repository declares @docsearch/react as ^3.5.2, but @docsearch/css@3 resolves a moving 3.x release from the CDN. The CSS and modal bundle can drift, which makes deployments non-reproducible. Use the exact CSS version validated against the resolved modal package, or bundle that version while keeping it lazy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/AlgoliaSearch.tsx` at line 331, Update the deferred stylesheet URL
in the AlgoliaSearch component to pin `@docsearch/css` to the exact version
matching the resolved `@docsearch/react` release, preserving lazy loading and
avoiding the moving `@3` CDN reference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/AlgoliaSearch.tsx`:
- Around line 2-26: Run the repository-configured Prettier formatter on
components/AlgoliaSearch.tsx, including the import and DocSearchModal
dynamic-import block and all other changed ranges, without altering behavior.
- Around line 321-355: Update the stylesheet-loading useEffect and the isOpen
rendering flow in the Algolia search component to track DocSearch link load and
error states, resetting readiness when opening and allowing retries after
failures. Render AlgoliaModal only once the stylesheet has fired load
successfully, while preserving the existing modal props and close behavior.

---

Nitpick comments:
In `@components/AlgoliaSearch.tsx`:
- Line 331: Update the deferred stylesheet URL in the AlgoliaSearch component to
pin `@docsearch/css` to the exact version matching the resolved `@docsearch/react`
release, preserving lazy loading and avoiding the moving `@3` CDN reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dee658f6-40de-4fe7-9768-37882191ea23

📥 Commits

Reviewing files that changed from the base of the PR and between 03fa4a6 and 5cc7bf6.

📒 Files selected for processing (2)
  • components/AlgoliaSearch.tsx
  • styles/globals.css

Comment on lines +2 to +26
import type {
DocSearchHit,
InternalDocSearchHit,
StoredDocSearchHit,
} from '@docsearch/react';
import clsx from 'clsx';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { createPortal } from 'react-dom';

const DocSearchModal = dynamic(
() =>
import('@docsearch/react').then((mod) => ({ default: mod.DocSearchModal })),
{ ssr: false },
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Run the configured formatter before merge.

prettier/prettier reports errors in this import and dynamic-import block, with the same issue in many later changed ranges. Format components/AlgoliaSearch.tsx with the repository configuration so lint can pass.

🧰 Tools
🪛 ESLint

[error] 2-6: Replace ⏎··DocSearchHit,⏎··InternalDocSearchHit,⏎··StoredDocSearchHit,⏎ with ·DocSearchHit,·InternalDocSearchHit,·StoredDocSearchHit·

(prettier/prettier)


[error] 12-19: Replace ⏎··createContext,⏎··useCallback,⏎··useContext,⏎··useEffect,⏎··useRef,⏎··useState,⏎ with ·createContext,·useCallback,·useContext,·useEffect,·useRef,·useState·

(prettier/prettier)


[error] 22-24: Replace ⏎··()·=>⏎····import('@docsearch/react').then((mod)·=>·({·default:·mod.DocSearchModal·})), with ()·=>·import('@docsearch/react').then((mod)·=>·({·default:·mod.DocSearchModal·})),·{

(prettier/prettier)


[error] 25-25: Replace ·{·ssr:·false·}, with ·ssr:·false

(prettier/prettier)


[error] 26-26: Insert }

(prettier/prettier)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/AlgoliaSearch.tsx` around lines 2 - 26, Run the
repository-configured Prettier formatter on components/AlgoliaSearch.tsx,
including the import and DocSearchModal dynamic-import block and all other
changed ranges, without altering behavior.

Source: Linters/SAST tools

Comment on lines +321 to +355
useEffect(() => {
if (isOpen) {
// Load Algolia CSS on demand when modal first opens
const linkId = 'docsearch-css';

if (!document.getElementById(linkId)) {
const link = document.createElement('link');

link.id = linkId;
link.rel = 'stylesheet';
link.href = 'https://cdn.jsdelivr.net/npm/@docsearch/css@3';
document.head.appendChild(link);
}
}
}, [isOpen]);

return (
<>
<Head>
<link rel='preconnect' href={`https://${APP_ID}-dsn.algolia.net`} crossOrigin='anonymous' />
<link
rel="preconnect"
href={`https://${APP_ID}-dsn.algolia.net`}
crossOrigin="anonymous"
/>
</Head>
<SearchContext.Provider value={{ isOpen, onOpen, onClose, onInput }}>{children}</SearchContext.Provider>
{isOpen && <AlgoliaModal initialQuery={initialQuery ?? ''} onClose={onClose} indexName={indexName} />}
<SearchContext.Provider value={{ isOpen, onOpen, onClose, onInput }}>
{children}
</SearchContext.Provider>
{isOpen && (
<AlgoliaModal
initialQuery={initialQuery ?? ''}
onClose={onClose}
indexName={indexName}
/>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Wait for the DocSearch stylesheet before mounting the modal.

When isOpen becomes true, React mounts AlgoliaModal before the effect appends and loads the stylesheet. The first open can show an unstyled modal. A failed request also prevents retries because the existing element ID is treated as success. Track stylesheet load and error states, and render the modal only after the stylesheet is ready.

🧰 Tools
🪛 ESLint

[error] 340-344: Replace ⏎··········rel="preconnect"⏎··········href={https://${APP_ID}-dsn.algolia.net}⏎··········crossOrigin="anonymous"⏎······· with ·rel='preconnect'·href={https://${APP_ID}-dsn.algolia.net}·crossOrigin='anonymous'

(prettier/prettier)


[error] 346-348: Replace ⏎········{children}⏎······ with {children}

(prettier/prettier)


[error] 349-355: Replace (⏎········<AlgoliaModal⏎··········initialQuery={initialQuery·??·''}⏎··········onClose={onClose}⏎··········indexName={indexName}⏎········/>⏎······) with <AlgoliaModal·initialQuery={initialQuery·??·''}·onClose={onClose}·indexName={indexName}·/>

(prettier/prettier)

🪛 GitHub Check: SonarCloud Code Analysis

[warning] 346-346: The object passed as the value prop to the Context provider changes every render. To fix this consider wrapping it in a useMemo hook.

See more on https://sonarcloud.io/project/issues?id=asyncapi_website&issues=AZ-2tu0BX1qv0iLRvOfI&open=AZ-2tu0BX1qv0iLRvOfI&pullRequest=5673

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/AlgoliaSearch.tsx` around lines 321 - 355, Update the
stylesheet-loading useEffect and the isOpen rendering flow in the Algolia search
component to track DocSearch link load and error states, resetting readiness
when opening and allowing retries after failures. Render AlgoliaModal only once
the stylesheet has fired load successfully, while preserving the existing modal
props and close behavior.

@github-actions github-actions Bot added the bounty AsyncAPI Bounty program related label label Jul 31, 2026
@aeworxet

Copy link
Copy Markdown
Contributor

@asyncapi/bounty_team

@lb1192176991-lab

Copy link
Copy Markdown

Submitted PR #5677 for this issue.

@aeworxet

aeworxet commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@kenxxxito-cmd, please note that the review of PRs falls to maintainers, and the code submitted is not always in a completed state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bounty AsyncAPI Bounty program related label

Projects

Status: In Progress
Status: To Be Triaged

Development

Successfully merging this pull request may close these issues.

[FEATURE] Performance + Accessibility Improvement of website

4 participants