Skip to content

chore: Explore other optimisations#396

Open
lee-chase wants to merge 6 commits intomainfrom
otherOptimisations
Open

chore: Explore other optimisations#396
lee-chase wants to merge 6 commits intomainfrom
otherOptimisations

Conversation

@lee-chase
Copy link
Member

NOTE: Builds on top of the codeSplitting branch.

This exploration looks at other optimizations.

Lighthouse before and after does not seem to have changed.

Optimization Summary

Optimizations Successfully Applied (Working)

  1. Brotli Compression (src/server.js)
    app.use(compression({ level: 6, brotli: { enabled: true, zlib: {} } }));

Goal: Reduce network transfer size by using modern Brotli compression algorithm instead of just gzip.

Impact: 15-25% better compression than gzip (~40-50 KB saved per page load)

  1. Fixed sideEffects (package.json)
    "sideEffects": ["/*.scss", "/*.css"]

Goal: Tell bundlers that CSS files have side effects (they execute when imported) so they won't be removed during tree-shaking, while allowing JavaScript modules to be tree-shaken safely.

Impact: Prevents CSS from being incorrectly tree-shaken while allowing JS tree-shaking

  1. CSS Optimization
    Switched IBM Products import to not include Carbon and unreleased styles.

Goal: Remove duplicated CSS.

Impact: Reduces CSS by 0.36MB

  1. CSS Organization (vite.config.js)
    assetFileNames: (assetInfo) => {
    if (assetInfo.name.endsWith('.css')) {
    return 'assets/css/[name]-[hash][extname]';
    }
    return 'assets/[name]-[hash][extname]';
    }

Goal: Organize build output by placing CSS files in a dedicated assets/css/ directory for better project structure and easier CDN configuration.

Impact: Better file organization (CSS in assets/css/ directory)

  1. Disabled CSS Code Splitting (vite.config.js)
    cssCodeSplit: false

Goal: Keep all CSS in a single bundle to ensure SSR has all styles available immediately, preventing render-blocking requests and FOUC (Flash of Unstyled Content).

Impact: Single CSS bundle for SSR (prevents render-blocking requests)

  1. Font Optimization Tooling (package.json)
    "optimize:fonts": "glyphhanger --subset=*.woff2 --formats=woff2"

Goal: Provide a tool to subset IBM Plex fonts by removing unused glyphs, reducing font file sizes by 50-70%.

Impact: Ready to use (run npm run optimize:fonts to subset IBM Plex fonts)

Optimizations That Proved Problematic

  1. Manual Chunk Splitting (REMOVED)
    // This caused hydration errors
    manualChunks: (id) => {
    if (id.includes('@carbon/react')) return 'carbon-vendor';
    if (id.includes('react')) return 'react-vendor';
    // etc...
    }

Goal: Separate Carbon Design System and React into dedicated chunks for better caching (so updates to app code don't invalidate the vendor cache).

Problems:

❌ React hydration error #418 (HTML mismatch)
❌ Broke SSR hydration timing
❌ Gray screen/CLS issues
Why it failed: Custom chunking interfered with React's SSR hydration order, causing the client-side React to mismatch with server-rendered HTML.

  1. CSS Code Splitting Enabled (REMOVED)
    // This caused render-blocking
    cssCodeSplit: true

Goal: Split CSS per route so each page only loads the CSS it needs, reducing initial bundle size and improving caching.

Problems:

❌ Multiple CSS files = render-blocking requests
❌ Lighthouse "Reduce unused CSS" warnings increased
❌ Slower SSR initial render
Why it failed: SSR needs all styles available immediately for proper rendering; splitting CSS created multiple render-blocking requests that delayed first paint.

  1. express-static-gzip Middleware (REMOVED)
    // This broke static file serving
    app.use(base, expressStaticGzip('./dist/client', {
    enableBrotli: true,
    orderPreference: ['br', 'gz']
    }));

Goal: Serve pre-compressed Brotli and gzip files directly from disk instead of compressing on-the-fly, reducing server CPU usage.

Problems:

❌ Blank screen on page load
❌ Static assets not serving correctly
❌ Broke SSR HTML route handling
Why it failed: This middleware completely replaced the sirv static file server, breaking the serving of HTML, CSS, and JS files needed for the application to load.

  1. Aggressive sideEffects Configuration (MODIFIED)
    // This was too specific
    "sideEffects": ["/*.scss", "/*.css", "src/index.scss"]

Goal: Explicitly list all files with side effects to give bundlers maximum information for tree-shaking optimization.

Problems:

❌ Potentially caused module resolution issues
❌ Over-specified (redundant patterns)
Why it failed: The specific src/index.scss entry was redundant (already covered by **/*.scss) and potentially caused confusion in module resolution.

Summary
What Works (Current Branch)
✅ Brotli compression via compression middleware - Reduces network transfer size
✅ Fixed sideEffects for CSS files only - Prevents CSS tree-shaking bugs
✅ Remove duplicate CSS from IBM Products import
✅ Single CSS bundle (no code splitting) - Optimizes SSR rendering
✅ Organized asset output - Better project structure
✅ Vite's default JS code splitting (automatic, route-based) - Automatic optimization without manual configuration
What Doesn't Work (Removed)
❌ Manual JavaScript chunk splitting - Attempted to improve caching but broke SSR hydration
❌ CSS code splitting - Attempted to reduce initial CSS but created render-blocking issues
❌ express-static-gzip middleware - Attempted to serve pre-compressed files but broke static serving
❌ Over-specific sideEffects patterns - Attempted to maximize tree-shaking but caused module resolution issues

Optimizations measurements

Testing Compression

Original: 1.35MB
Gzip: 130.19KB (90.6% reduction)
Brotli: 82.78KB (94.0% reduction)

CSS Size:

Before: 1.71MB
After: 1.35MB
Change: 20.9%

JavaScript Size:

Before: 511.20KB
After: 511.16KB
Change: 0%

Total Build Size:

Before: 3.49MB
After: 3.13MB
Change: 10.2%

Chunk Count:

CSS files: 2 → 1
JS files: 8 → 8

@lee-chase lee-chase requested a review from rodet as a code owner January 26, 2026 11:13
@lee-chase lee-chase changed the title chore: Explor other optimisations chore: Explore other optimisations Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant