Skip to content

fix: bundle size optimization#2849

Open
tomaszantas wants to merge 9 commits into
langfuse:mainfrom
Altalogy:fix/bundle-size-optimization
Open

fix: bundle size optimization#2849
tomaszantas wants to merge 9 commits into
langfuse:mainfrom
Altalogy:fix/bundle-size-optimization

Conversation

@tomaszantas
Copy link
Copy Markdown
Contributor

@tomaszantas tomaszantas commented Apr 23, 2026

Improve bundle size:

  1. Dynamic import of three.js and recharts
  2. Allow for lower quality images on the homepage
  3. Compress largest static images

Homepage /

Category PROD STAGING Δ br Δ % br Δ raw Δ % raw
HTML 35.1 KB / 407 KB 35.4 KB / 458 KB +0.3 KB +0.9% +51 KB +12.5%
JS (41→39 chunks) 1,187.6 KB / 3,976.6 KB 826.8 KB / 2,692.1 KB −360.8 KB −30.4% −1,284.5 KB −32.3%
Fonts (2) 102.2 KB / 102.2 KB 102.2 KB / 102.2 KB 0 0% 0 0%
Images (77) 1,878.8 KB / 1,991.5 KB 562.0 KB / 674.7 KB −1,316.8 KB −70.1% −1,316.8 KB −66.1%
External scripts (2) 9.4 KB / 27.1 KB 9.4 KB / 27.1 KB 0 0% 0 0%
TOTAL 3,213.0 KB / 6,504.0 KB 1,535.7 KB / 3,554.0 KB −1,677.3 KB −52.2% −2,950.0 KB −45.4%

/docs

Category PROD STAGING Δ br Δ % br Δ raw Δ % raw
HTML 41.4 KB / 383 KB 41.5 KB / 437 KB +0.1 KB +0.3% +54 KB +14.0%
JS (44→42 chunks) 1,268.2 KB / 4,219.3 KB 907.2 KB / 2,937.9 KB −361.0 KB −28.5% −1,281.4 KB −30.4%
Fonts (2) 102.2 KB / 102.2 KB 102.2 KB / 102.2 KB 0 0% 0 0%
Images (6) 117.0 KB / 121.6 KB 117.0 KB / 121.6 KB 0 0% 0 0%
External scripts (2) 9.4 KB / 27.1 KB 9.4 KB / 27.1 KB 0 0% 0 0%
TOTAL 1,538.2 KB / 4,853.5 KB 1,177.3 KB / 3,625.7 KB −360.9 KB −23.5% −1,227.8 KB −25.3%

Note:

  • HTML grew ~50 KB raw on staging but only +0.1–0.3 KB br — the delta is Vercel preview's ?dpl=<id> cache-busting suffix on every internal URL. Preview-environment artifact; disappears when staging merges to langfuse.com.
  • Network timings are identical (Staging vs Production) (~150 ms TTFB, ~190 ms total across 5 samples)

Disclaimer: Experimental PR review

Greptile Summary

This PR delivers three independent bundle-size improvements: correct dynamic splitting of Three.js (404 page) and recharts (/wrapped), removal of quality={100} overrides so Next.js's default 75% JPEG compression applies on homepage images, and lossless/near-lossless compression of static PNGs. The net result is a ~52% reduction in total homepage transfer size and ~23% reduction on /docs, per the benchmark table in the description. All changes are mechanically sound — the previous Promise.resolve(Component) anti-pattern (which prevented real code-splitting) is correctly replaced with a true dynamic import().

Confidence Score: 5/5

Safe to merge — all remaining findings are minor style suggestions with no correctness impact.

No P0 or P1 issues found. The bundle-split logic is correct, image handling is valid (StaticImageData sources auto-supply dimensions), and the binary image replacements are transparent swaps. The two P2 comments (duplicated constant, missing loading skeleton) are cleanup suggestions that do not affect runtime correctness.

No files require special attention.

Important Files Changed

Filename Overview
components/NotFoundAnimation.tsx Correctly fixes the bundle-splitting bug: the previous Promise.resolve(Component) trick did NOT code-split; the real dynamic import("./NotFoundAnimationImpl") now does.
components/NotFoundAnimationImpl.tsx Three.js implementation correctly extracted; CANVAS_STYLES constant is duplicated between this file and NotFoundAnimation.tsx — minor redundancy.
components/wrapped/Metrics.tsx Recharts removed from the main chunk via two correct dynamic imports; both charts resolve from the same module so the module is loaded only once by the bundler.
components/wrapped/MetricsCharts.tsx New file correctly contains all recharts data and components; ssr: false on the parent dynamic call means charts are client-only which is appropriate for a /wrapped page.
components/home/AllTheTools.tsx Safe: all tool.visual values are StaticImageData imports, so Next.js Image infers width/height automatically even without explicit props; sizes is correctly added and unoptimized removed.
components/home/feature-tabs/FeatureTabs.tsx Drops quality={100} overrides so Next.js default quality (75) applies, reducing image payload with negligible visual impact at these sizes.
components/home/feature-tabs/TabContent.tsx Same quality={100} removal pattern as FeatureTabs.tsx; change is safe and correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Browser requests page] --> B{Page route}

    B --> |"/ (homepage)"| C[Initial JS bundle]
    B --> |"/wrapped"| C
    B --> |"/404"| C

    C --> D[AllTheTools — static images\nNext.js optimized via sizes hint]
    C --> E[FeatureTabs / TabContent\nquality default 75 instead of 100]

    C --> F{Dynamic import\non demand}

    F --> |"404 page renders"| G["NotFoundAnimationImpl chunk\n(Three.js + @react-three/*)"]
    F --> |"/wrapped page renders"| H["MetricsCharts chunk\n(recharts ~100 KB br)"]

    G --> I[Three.js canvas mounted\nssr: false]
    H --> J[ConsumptionChart rendered\nssr: false]
    H --> K[DownloadsChart rendered\nssr: false]

    style G fill:#f9a,stroke:#c66
    style H fill:#f9a,stroke:#c66
    style D fill:#9f9,stroke:#696
    style E fill:#9f9,stroke:#696
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: components/NotFoundAnimationImpl.tsx
Line: 8-13

Comment:
**Duplicated `CANVAS_STYLES` constant**

`CANVAS_STYLES` is defined identically in both `NotFoundAnimation.tsx` (lines 9–13) and this new `NotFoundAnimationImpl.tsx`. Since the loading placeholder in `NotFoundAnimation.tsx` uses an inline `style={CANVAS_STYLES}` object and the real canvas uses the same constant here, they could share a single source of truth (e.g. a shared `constants.ts` or by re-exporting from the impl file). Not a runtime issue, but any future dimension tweak needs to be applied in two places.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: components/wrapped/Metrics.tsx
Line: 15-26

Comment:
**No loading fallback for dynamically imported charts**

Both `ConsumptionChart` and `DownloadsChart` are imported with `{ ssr: false }` but no `loading` option. On first client render the chart containers (which have a fixed `lg:h-[400px]` / `aspect-[21/9]` height) will be empty until the `MetricsCharts` chunk is fetched and evaluated. Adding a lightweight skeleton prevents the jarring blank-then-chart flash:

```tsx
const ConsumptionChart = dynamic(
  () => import("./MetricsCharts").then((m) => ({ default: m.ConsumptionChart })),
  { ssr: false, loading: () => <div className="w-full h-full animate-pulse bg-muted rounded-lg" /> }
);
const DownloadsChart = dynamic(
  () => import("./MetricsCharts").then((m) => ({ default: m.DownloadsChart })),
  { ssr: false, loading: () => <div className="w-full h-full animate-pulse bg-muted rounded-lg" /> }
);
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: optimized images (#106)" | Re-trigger Greptile

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

@tomaszantas is attempting to deploy a commit to the langfuse Team on Vercel.

A member of the Team first needs to authorize it.

@tomaszantas tomaszantas marked this pull request as ready for review April 23, 2026 10:16
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Apr 23, 2026
@felixkrrr
Copy link
Copy Markdown
Contributor

@claude review

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
langfuse-docs Ready Ready Preview, Comment Apr 24, 2026 0:31am

Request Review

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Clean bundle-size optimization — dynamic imports for three.js and recharts are correctly wired, image hints are appropriate, and the removals fall back to Next.js's default of 75. Worth a visual spot-check on the preview, but no correctness concerns.

Extended reasoning...

Overview

The PR makes three independent, mechanical optimizations across 15 files: (1) splits Three.js into a dynamic chunk by extracting the real implementation into NotFoundAnimationImpl.tsx and replacing the no-op Promise.resolve(Component) pattern with a true import(), (2) splits recharts into MetricsCharts.tsx and dynamically imports both charts into Metrics.tsx (used only on /wrapped), and (3) drops quality={100}/unoptimized props on homepage images so Next.js's default compression and optimization pipeline runs. Seven PNG binary assets are also recompressed.

Security risks

None. All changes are client-side bundling, image optimization props, and static binary swaps. No touching of auth, crypto, permissions, data handling, API surfaces, or user input. This is a docs/marketing site, not production backend.

Level of scrutiny

Low. Each logical change is a well-documented Next.js pattern: dynamic() with ssr: false for client-only heavy deps, named re-exports via .then((m) => ({ default: m.X })), StaticImageData imports auto-supplying width/height to <Image>, and sizes hints for responsive optimization. The Promise.resolve(Component) → real import() conversion is a correct bug fix — the prior code was a known anti-pattern that defeated code-splitting.

Other factors

Bug hunter found no issues. Greptile reviewed with 5/5 confidence; its two P2 notes (duplicated CANVAS_STYLES constant between NotFoundAnimation.tsx and NotFoundAnimationImpl.tsx; no loading skeleton on the dynamic charts) are nits that don't affect correctness. The quality=75 default is the Next.js / web-perf industry norm and the payload deltas (~52% total homepage reduction, ~23% on /docs) are substantial and measured. Since image quality is inherently subjective, a quick visual check on the Vercel preview is advisable before merging, but that is a UX judgment call rather than a code-correctness issue.

Comment on lines +19 to +30
// Dynamic import keeps recharts out of every page's initial chunk graph.
// The charts only render on /wrapped, so there's no reason to ship ~100 KB
// brotli of recharts on /, /docs/*, /pricing, etc.
const ConsumptionChart = dynamic(
() => import("./MetricsCharts").then((m) => ({ default: m.ConsumptionChart })),
{ ssr: false }
);
const DownloadsChart = dynamic(
() => import("./MetricsCharts").then((m) => ({ default: m.DownloadsChart })),
{ ssr: false }
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would these otherwise show up in the main bundle? It looks like components/wrapped/Metrics.tsx is only imported from content/marketing/wrapped.mdx. Do content routes hoist all their imports to the main content page?

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

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants