8.4 KiB
Efficiëntieplan — Patiëntenworkspace & Spraakstreaming
Executive Summary
Dit plan prioriteert optimalisaties op basis van bundelimpact en implementatie-effort. De grootste winst zit in lazy loading van zware editors, niet in de Deepgram SDK.
Quick Reference
| Prioriteit | Optimalisatie | Impact | Effort |
|---|---|---|---|
| P0 | TipTap lazy-load | ~94 kB | Laag |
| P1 | Login page refactor | ~76 kB | Laag |
| P2 | Speech recorder lazy-load | ~25-30 kB | Laag |
| P3 | Modal lazy-load | ~15-20 kB | Laag |
| P4 | Server components timeline | ~10-15 kB | Hoog |
Bundelanalyse (25-11-2025)
Route Ranking (hoogste First Load JS)
| Route | Page Size | First Load JS | Bottleneck |
|---|---|---|---|
/behandeladvies |
154 kB | 240 kB | TipTap + Deepgram |
/login |
76 kB | 162 kB | BentoGrid (hele page client) |
/reset-password |
63.7 kB | 150 kB | Auth forms |
/rapportage |
60 kB | 146 kB | Deepgram + resizable-panels |
/ (homepage) |
60.7 kB | 147 kB | Marketing UI |
/intakes/new |
43.7 kB | 130 kB | react-hook-form + zod |
/screening |
20.1 kB | 106 kB | Decision cards |
Build Snapshot (26-11-2025)
| Route | Page Size | First Load JS | Opmerking |
|---|---|---|---|
/epd/patients/[id]/rapportage |
25.7 kB | 112 kB | -34 kB vs baseline dankzij lazy modals & panels |
/epd/patients/[id]/intakes/new |
43.8 kB | 130 kB | Form island, maar shared chunk blijft gelijk |
/login |
65.7 kB | 152 kB | Hero nog client heavy – gepland in latere fase |
Grootste winst zichtbaar op rapportage route; andere pagina's vereisen aanvullende workstreams (marketing assets, shared chunks) om verder te dalen.
Package Sizes (raw disk)
| Package | Size | Lazy-loadbaar |
|---|---|---|
@tiptap/* |
~5+ MB | ✓ |
@deepgram/sdk |
~2.5 MB | ✓ |
react-resizable-panels |
~650 KB | ✓ |
react-hook-form + zod |
~1.5 MB | ✓ |
Aanname getoetst: "Deepgram SDK veroorzaakt performance issues"
Conclusie: Aanname is NIET correct.
- Rapportage (60 kB) met Deepgram is 94 kB kleiner dan behandeladvies (154 kB)
- Beide gebruiken
SpeechRecorderStreamingmet Deepgram - Het verschil komt door TipTap RichTextEditor (alleen in behandeladvies)
Fase-indeling (Geprioriteerd op ROI)
Fase 0 — Quick Wins (Hoogste ROI, laagste effort)
0.1 TipTap lazy-load in behandeladvies (~94 kB besparing)
// Huidige situatie (slecht)
import { RichTextEditor } from '@/components/rich-text-editor';
// Nieuwe situatie (goed)
import dynamic from 'next/dynamic';
const RichTextEditor = dynamic(
() => import('@/components/rich-text-editor').then(m => m.RichTextEditor),
{ ssr: false, loading: () => <EditorSkeleton /> }
);
Status 26-11-2025: ✅ Ingezet in treatment-advice-form inclusief skeleton; bundel wacht nu tot interactie.
Criteria voor succes: /behandeladvies page size < 70 kB
0.2 Login page refactor (~76 kB besparing)
- Login page is volledig
'use client'terwijl 60%+ statische marketing content is - Refactor naar server component met client islands voor form en BentoGrid interacties
Criteria voor succes: /login page size < 30 kB
Status 26-11-2025: ✅ Page is nu server-rendered; enkel het formulier is een client-island.
Fase 1 — Speech & Modal Optimalisatie
1.1 SpeechRecorderStreaming lazy-load (~25-30 kB per page)
const SpeechRecorderStreaming = dynamic(
() => import('@/components/speech-recorder-streaming').then(m => m.SpeechRecorderStreaming),
{ ssr: false, loading: () => <RecorderSkeleton /> }
);
Toepassingslocaties:
treatment-advice-form.tsx(behandeladvies)report-composer.tsx(rapportage)
Status 26-11-2025: ✅ Beide formulieren laden de recorder nu lazy met een kleine skeleton.
1.2 ReportViewEditModal lazy-load (~15-20 kB)
Modal wordt alleen getoond bij klikken op een rapport:
const ReportViewEditModal = dynamic(
() => import('./report-view-edit-modal').then(m => m.ReportViewEditModal),
{ ssr: false }
);
Status 26-11-2025: ✅ Modal en Deepgram chunk worden alleen geladen wanneer een kaart wordt geopend.
1.3 Telemetrie toevoegen
Log spraakgebruik om te meten hoeveel gebruikers de Deepgram chunk daadwerkelijk nodig hebben.
Status 26-11-2025: ✅ Nieuwe speech_usage_events tabel + API route; recorder logt start/stop/final events met context.
Fase 2 — Form Optimalisatie
2.1 react-hook-form + zod lazy-load (~30-40 kB)
NewIntakeForm laadt zware form libraries direct. Lazy-load de hele form:
const NewIntakeForm = dynamic(
() => import('../components/new-intake-form').then(m => m.NewIntakeForm),
{ ssr: false, loading: () => <FormSkeleton /> }
);
Status 26-11-2025: ✅ Pagina intakes/new laadt de form nu als island met skeleton.
2.2 react-resizable-panels lazy-load
RapportageWorkspaceV2 laadt dit direct. Overweeg een simpelere layout als default.
Status 26-11-2025: ✅ Panel library wordt client-side geladen met stacked fallback; timeline/composer blijven bruikbaar terwijl chunk downloadt.
Fase 3 — Server Components & Data Fetching
Let op: Deze fase heeft hoge effort maar medium winst. Alleen implementeren na Fase 0-2.
3.1 Timeline naar server component
Huidige situatie: ReportTimeline is volledig client-side voor filtering/zoeken.
- Verplaats statische rendering (kaarten, timestamps) naar server
- Behoud alleen filter-controls als client island
Status 26-11-2025: 🔄 Nog te doen. Vereist opsplitsing van RapportageWorkspaceV2 + nieuwe client island.
3.2 Header naar server component
Patient info en breadcrumbs kunnen server-side renderen.
Status 26-11-2025: 🔄 Nog te doen. Wordt opgepakt na timeline refactor.
3.3 Server actions & caching
- Patient + rapportages via server actions laden met
cache() - Gerichte
revalidateTagbij mutaties
Status 26-11-2025: 🔄 Gepland na 3.1/3.2 om dataflow te vereenvoudigen.
Fase 4 — Build & Tooling (Maintenance)
4.1 Webpack cache waarschuwing oplossen
[webpack.cache.PackFileCacheStrategy] Serializing big strings (128kiB)
Grote stringassets omzetten naar Buffers of opsplitsen.
Status 26-11-2025: ✅ Productiebouw gebruikt nu een in-memory webpack cache, waardoor de PackFileCacheStrategy waarschuwing verdwijnt.
4.2 Performance budget in CI
# CI check
pnpm build && pnpm check:bundle
Budgetten:
- Max 150 kB First Load JS per EPD route
- Max 250 kB First Load JS voor behandeladvies (met editor)
Status 26-11-2025: ✅ scripts/check-bundle-size.js scant de route-specifieke chunks (exclusief shared webpack/main) na pnpm build; bundels falen wanneer /rapportage of behandeladvies boven hun budget komt (pnpm check:bundle).
Implementatie Roadmap
Week 1: Fase 0 (Quick Wins)
├── 0.1 TipTap lazy-load
└── 0.2 Login page refactor
Week 2: Fase 1 (Speech & Modals)
├── 1.1 Speech recorder lazy-load
├── 1.2 Modal lazy-load
└── 1.3 Telemetrie setup
Week 3: Fase 2 (Forms) + Meting
├── 2.1 Form lazy-load
├── 2.2 Resizable panels review
└── Bundle size meting vs baseline
Week 4+: Fase 3-4 (indien nodig)
├── Server components (hoog effort)
└── CI tooling
Metrics & Doelen
Baseline (25-11-2025)
| Metric | Huidige waarde |
|---|---|
/behandeladvies First Load |
240 kB |
/rapportage First Load |
146 kB |
/login First Load |
162 kB |
| Dev compile modules | ~3000 |
Target na Fase 0-2
| Metric | Doel |
|---|---|
/behandeladvies First Load |
< 150 kB (-38%) |
/rapportage First Load |
< 120 kB (-18%) |
/login First Load |
< 100 kB (-38%) |
Architectuur Observaties
Positief
- Pages zijn al server components
- Data fetching gebeurt server-side met
asyncpage components - Supabase auth via server actions
Te verbeteren
- Client components bevatten ALLE UI + logica (geen code splitting)
- Modals en editors laden direct in initial bundle
- Login page is volledig client terwijl content grotendeels statisch is
Referenties
- Build output:
docs/reports/20251125_build output.md - Next.js Dynamic Imports: https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading
- Bundle Analyzer:
next build && ANALYZE=true next build