Skip to content

Code-split the spreadsheet engine out of the document WASM bundle #30

Open

Description

@jjbaumert

Parent track: #21 (bundle-size ratchet-down). Split out per the 2026-07-10 investigation there — this is the one real bundle-reduction lever found, but it's a multi-day architectural project, not a gate chore, so it lives on its own.

Summary

The spreadsheet engine + its UI compile into the same single *_bg.wasm as the document editor and load on every page, even pure-document sessions that never open a sheet. Deferring it behind a dynamic load is the largest available WASM reduction.

Size (rough, from the #21 twiggy investigation)

  • Engine (src/spreadsheet/{eval,functions,parser,pivot,stats,translate}.rs) + UI (src/components/spreadsheet_view.rs) = 294,746 B raw pre-opt = 5.3% of real code / ~25% of our own app code.
  • Proportional estimate on the shipped baseline (~1,712,714 B gzip): ~85–90 KB gzipped saved on non-spreadsheet page loads.
  • Heavily caveated: linear pre-opt→post-opt-gzip scaling is not exact; treat as order-of-magnitude, not a promise. A precise number needs wasm-opt with names preserved through the pipeline (not currently installed).
  • Single biggest function in the engine: spreadsheet::functions::call_function = 124,301 B (a large match over every sheet function) — a secondary, self-contained target if the split proves too heavy.

Why it's not a quick change (separability blocker)

  • src/pages/document.rs imports SpreadsheetView unconditionally (use crate::components::spreadsheet_view::SpreadsheetView;) and branches on doc_type at runtime inside one page component — there is no module boundary isolating spreadsheet code from the document page today.
  • Genuine lazy-loading requires either:
    1. A second wasm-bindgen target compiled to its own .wasm + JS-side import() glue, plus a way to bridge Leptos reactive state across two wasm instances; or
    2. Restructuring pages::document.rs to defer the SpreadsheetView import behind a dynamic-loading shim.
  • Neither exists in the current Trunk / Leptos-CSR setup. This is the bulk of the work.

Don't forget the CSS

spreadsheet.css (~37 KB, frontend/dist/spreadsheet-*.css) is loaded unconditionally via a static <link> in index.html — outside the WASM gate. Splitting the WASM without also deferring this CSS only captures part of the win.

Severity

S4 — no user-facing issue; a bundle-weight optimization. Deferred: high effort, medium-uncertain payoff, and even a perfect split leaves the bundle at ~1.61 MB (Leptos framework internals are ~34.5% of the bundle and structurally dominate — see #21). Pick up when someone has multi-day appetite for the CSR code-splitting infra, not under gate pressure.

Suspected area

  • frontend/src/pages/document.rs — the unconditional import + runtime doc_type branch
  • frontend/src/components/spreadsheet_view.rs, frontend/src/spreadsheet/ — the code to split
  • frontend/index.html — static spreadsheet CSS <link>
  • frontend/Trunk.toml / build config — where a second wasm target / dynamic import would be wired

Activity

  1. added a commit that references this issue on Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions