惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cisco Talos Blog
Cisco Talos Blog
L
Lohrmann on Cybersecurity
AWS News Blog
AWS News Blog
J
Java Code Geeks
博客园_首页
Scott Helme
Scott Helme
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
T
The Exploit Database - CXSecurity.com
Security Latest
Security Latest
V
Visual Studio Blog
Cloudbric
Cloudbric
Jina AI
Jina AI
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
C
Cybersecurity and Infrastructure Security Agency CISA
S
SegmentFault 最新的问题
The Last Watchdog
The Last Watchdog
SecWiki News
SecWiki News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
W
WeLiveSecurity
K
Kaspersky official blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hacker News: Ask HN
Hacker News: Ask HN
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
量子位
Google Online Security Blog
Google Online Security Blog
博客园 - Franky
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 三生石上(FineUI控件)
Recent Commits to openclaw:main
Recent Commits to openclaw:main

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Frankenstein Meeting Room: Three Apps in One Browser Tab
Lutz Leonhar · 2026-05-13 · via DEV Community

🇩🇪 Auf Deutsch lesen: Frankenstein Meeting Room: Drei Apps in einem Browser-Tab

1. Intro — Why Frankenstein?

The 2010s were the great frontend war. Many new UI frameworks emerged in this period and disappeared again. You can see this in today's enterprises' legacy applications. Many have Angular, React, Vue, or Svelte in use for various use cases. If you want to use these applications not as islands but in their entirety, the only option is usually to migrate to a common framework. A long-running and error-prone process. A better option can be to let the apps communicate with each other via a common orchestrator platform. That is the starting point for our Frankenstein Meeting Room based on Native Federation v4.

2. What the app does

The setup is meant to show how a heterogeneous web app landscape (Angular, React, Svelte) can be integrated via Native Federation. The project simulates a legacy enterprise landscape in miniature. Concretely: In an Angular 21 calendar (shell), meetings can be selected. The linked information is displayed in a Svelte 5 Mermaid diagram (Remote 1) as well as a React 18 Excalidraw whiteboard (Remote 2). When switching the meeting, the data is saved and loaded (LocalStorage).

The demo runs at lutzleonhardt.de/frankenstein-meeting-room, the code is on GitHub.

3. Starting point and workflow

In the previous article I already presented the specification of the implementation and the UI mockup (Claude Design).

The original UI mockup from Part 1, generated with Claude Design. The lab notebook look made it into the finished app.

For the implementation I used my Skill Kit for agentic workflows together with the code agents Claude Code and Codex in tandem. The agents translated the specification into individual milestone plans — in the end 18 tasks across 6 milestones. Each milestone plan was broken down into tasks. The advantage of this approach: each milestone produces a verifiable artifact that the developer can validate and review in isolation. The result then serves as the basis for the next step. For each task not only the code is produced, but also a task log: what was tried, what was discarded, which hypotheses died along the way. This negative information is almost always lost in normal commit bodies — while writing this post I had to look things up there most often.

The NF builder doesn't release the process. At your own terminal you don't notice it. You hit Ctrl-C and move on. In the agentic workflow this becomes a problem: the agent doesn't know that the work is done and keeps waiting. The workaround was a small wrapper script: delete the artifact beforehand, start ng build, poll for the artifact, then send SIGKILL to the entire process group.

4. Native Federation: Shell, Remotes, Bus

At the center of Native Federation is the shell (also called host), which loads the other web apps or exported UI components as remotes from remote endpoints. In our case the remotes are standalone web apps (Svelte, React), each of which exports a custom element for the shell.

Since no internal binding can be used for communication between the heterogeneous frameworks, an agnostic messaging pattern must be established. In the case of Frankenstein Meeting Room I decided on a simple, self-implemented pub-sub bus. The topology is star-shaped: the remotes cannot communicate with each other.

The bus implementation lies in the shared area of the monorepo, shell and remotes each import the functionality themselves. The actual bus is attached as a singleton to globalThis (window). The BusEvents were typed as DeepReadonly<T> to prevent accidental mutation across the framework boundary at compile time. A deep clone would be too costly: the entire Excalidraw data structure would have had to be cloned.

// packages/shared/src/bus.ts
type BusEvents = {
  'context:request': {};
  'event:selected': { meetingId: string; initialData: Meeting };
  'drawing:changed': { meetingId: string; excalidrawData: ExcalidrawDemoData };
  'diagram:changed': { meetingId: string; mermaidSource: string };
};
const bus = (globalThis.frankensteinBus ??= new EventTarget()) as EventTarget;
export function emit<K extends keyof BusEvents>(
  name: K, payload: DeepReadonly<BusEvents[K]>,
) {
  bus.dispatchEvent(new CustomEvent(name, { detail: payload }));
}

Enter fullscreen mode Exit fullscreen mode


Star-shaped topology: all bus communication goes through the center, persistence lives with the host. Rendered live in the Svelte Mermaid editor of the demo itself.

5. M1 — Monorepo, shared package, shell skeleton

In the first milestone I set up the pnpm monorepo and implemented the types as well as the bus in the shared package. Via a Native Federation schematic the Angular project is then converted into a Native Federation shell. At the core, the project gets an adapted build, a slightly modified bootstrap process, and a federation configuration. The bootstrap runs in two stages: the shell first loads the federation manifest, injects the import map, and only then starts the actual Angular app. The configuration defines which dependencies are shared between shell and remotes.

That's another advantage of Native Federation: depending on the configuration, libraries are loaded only once, even if multiple remotes and the shell use them.

6. M2 — Host complete: calendar, meeting service, panels

In milestone 2 I completed the shell by adding the calendar (Schedule-X). There is also a meeting service that manages the current meetings based on signals, receives messages via the event bus, and sends data, for example to initialize the remotes with the active meeting. Initially the meetings are populated by a seed. On changes they are saved in LocalStorage and loaded from there again on start. In the shell there are two more Angular components: the detail view for the active meeting as well as an overview of the bus messages. The layout is three-column: calendar on the left, remotes in the middle, detail view and bus log on the right.

A small detail in the meeting service: the remotes debounce their updates (500 ms), and without a guard a meeting switch in the middle of the debounce would lead to the old draft overwriting the new meeting. The fix: every applyDrawingChange/applyDiagramChange in the service checks the meetingId against currentMeeting and drops stale updates. One line of code, without which the bus would have had subtle data corruption.

private applyDrawingChange(p: DrawingChangedPayload): void {
  if (p.meetingId !== this.currentMeeting()?.id) return; // stale-update guard
  // ... persist
}

Enter fullscreen mode Exit fullscreen mode

In the middle area of the shell the React and Svelte applications now still need to be integrated.

7. M3 & M4 — Whiteboard and Mermaid as remotes

In milestones 3 and 4 I implemented the React whiteboard (Excalidraw) and the Svelte Mermaid diagram each as a remote. For both I proceeded the same way: first the standalone app, then placed the federation configuration on top. That way I could develop and test Excalidraw or the Mermaid diagram in isolation before the remote was integrated into the shell. The same UI project (react, svelte) continues to run under its own port as a standalone app and can at the same time be loaded by the host as a remote — for the federation loading, however, the built JavaScript chunks plus remoteEntry.json as static assets are sufficient, a running dev server is not necessary (only for the standalone test).

@frankenstein/shared as devDependency, not as dependency. That way shareAll skips the package, and the globalThis singleton from Section 4 stays intact — the conceptually most important build-mode decision of the project.

Excalidraw spam without real changes. Excalidraw's onChange fires even without real changes. When resizing the window I got ~8 drawing:changed events in 11 seconds without anyone having drawn anything. I had already built in the 500 ms debounce at the sender — but it only groups the events together, it doesn't recognize whether anything has actually changed. The real fix was a fingerprint over ${element.id}:${element.version}: Excalidraw only bumps version on real changes, not on cosmetic reflows. With that the empty events fall out before the debounce, and afterwards it only takes care of the frequency of real edits.

const fp = elements.map(e => `${e.id}:${e.version ?? 0}`).join('|');
if (fp === prevFingerprintRef.current) return; // skip cosmetic re-renders
prevFingerprintRef.current = fp;
// → 500ms debounce → emit('drawing:changed', …)

Enter fullscreen mode Exit fullscreen mode

Firefox canvas limit. Firefox caps <canvas> at ~11180 px edge length. Since Excalidraw's stylesheet was only loaded after the React mount, the container was briefly large; Excalidraw computed a canvas size beyond the limit, and the first setTransform call threw an exception with a stack trace in the console — Excalidraw didn't even boot. Chromium doesn't have the limit: classic "works on my machine" bug. Solved by injecting the stylesheet into the head already at module init and having the first render wait for its load event.

The federation configuration is set up the same way for both remotes: a federation.config.mjs in which it is specified what the remote exports and which libraries are shared. Shared libraries are usually loaded as singletons — exactly one instance for shell and all remotes, even though each project brings them in as a dependency itself. Version conflicts are resolved at build time, transitive sub-dependencies are explicitly shared or excluded. With that the federation configuration ultimately also determines how many JavaScript chunks are produced per application.

React jsx-runtime as CJS. React still ships some modules as CommonJS, which Native Federation converts to ESM during the build. With one specific pattern — react/jsx-runtime — this translation doesn't work cleanly: the jsx function was undefined at runtime and Excalidraw blew up on the first render. Fixable via path mapping in the federation configuration, which skips the problematic intermediate step and points directly to React's pre-compiled CJS file.

Svelte twice in the tab. In the Mermaid remote it was the other way around: Svelte couldn't be shared cleanly at all. Its internal code references itself via relative paths that the federation mechanism can't reach — in the end two Svelte runtimes ended up in the tab in parallel, and Mermaid threw effect_orphan at runtime. Pragmatically solved: Svelte out of the share map and bundled directly into the Mermaid bundle. With only one Svelte app the size doesn't matter — the ~160 kB ends up on the client either way, whether as a shared chunk next to the bundle or as part of it. Only from two Svelte remotes onwards would the duplication start to bite.

8. Islands instead of components

In a homogeneous landscape — that is, when all apps use the same UI framework — you would export components directly and use them in the shell. In a heterogeneous landscape this doesn't work: if I only exported the React component from the React project, the Angular shell could not load it because the React runtime is simply missing in the tab. A pure component has no platform underneath it.

The solution is islands: each remote exports not just the component, but the complete app including its framework. In the tab one UI framework then runs per island, fully encapsulated. Technically this happens via native custom elements: the remote defines a <whiteboard-remote> (or <mermaid-remote>), the shell renders the tag like any other DOM element, and in the connectedCallback the embedded framework boots. That's exactly how whiteboard and Mermaid are implemented in milestones 3 and 4.

class WhiteboardRemote extends HTMLElement {
  connectedCallback() {
    this.root = createRoot(this);
    this.unsubs.push(on('event:selected', ({ initialData }) => {
      this.render(initialData);
    }));
    emit('context:request', {});
  }
  disconnectedCallback() {
    this.unsubs.forEach(u => u());
    this.root?.unmount();
  }
}
customElements.define('whiteboard-remote', WhiteboardRemote);

Enter fullscreen mode Exit fullscreen mode


React DevTools see the full component tree (left). Wappalyzer finds no React (right). The island works.

9. Bus access and events

Shell and remotes each import the bus directly from the shared package (see Section 4). The central, non-trivial part is the init handshake between remote and host: On mount a remote doesn't yet know which meeting is currently active — the host holds the state. So after its initialization the remote sends a context:request over the bus and gets back from the host an event:selected with the current meeting. Only with that does it know which whiteboard or Mermaid data it should load. The two remaining events (drawing:changed, diagram:changed) are pure change notifications from the remotes towards the host.


The event bus log live: context:request on mount of a remote, event:selected as host response, then drawing:changed for an actual whiteboard change.

Deliberately not in the setup: creating or editing meetings via the bus. That would have pulled CRUD wiring through all three frameworks without adding anything to the integration pattern itself. What is possible with Native Federation can be shown clearly with this minimal event set as well.

10. M5 & M6 — Polish and deployment

The last two milestones were about polish and deployment. M5 was fine-tuning: improved the CSS, did cleanups, created a representative seed. The seed has a small punchline: instead of lorem ipsum meetings, the demo meeting is an "Architecture Review" of the demo itself. The whiteboard draws three boxes plus bus, Mermaid shows the sequence diagram of the bus flow. The hero image of the post follows from that on its own.

M6 was the deployment to https://lutzleonhardt.de/frankenstein-meeting-room/ — all static under a subpath, no backend, plus the two remotes separately under /whiteboard/ and /mermaid/ as standalone apps. During development each remote ran via its own standalone dev server, the federation manifest pointed to localhost URLs accordingly. After the build the remotes migrate as static assets into the subpaths of the host, and the manifest is adjusted to match.

11. Take-away

The whole setup stood in 10 to 12 hours of net effort, with the help of the agentic process and consistent curation. Extending it is easy: further React applications can be plugged in just like an additional framework, for example Vue. That makes Native Federation a good fit for bringing a grown heterogeneous legacy landscape together onto one platform without having to pull off a big migration.

What the setup deliberately does not cover: authentication and authorization, cross-origin hardening, multi-user collaboration, contract versioning between host and remotes, e2e tests across island boundaries, mobile layouts. These are not the hard NF topics — but the topics that come in addition to NF in a real enterprise migration. The full delimitation is in the README.

Particular pitfalls exist mainly when multiple versions of the same framework run in parallel — for example Angular 15 and Angular 17 in the same platform. Then you have to pay close attention to shared dependencies, sub-dependencies, and transitive dependencies. The loading of dependencies runs centrally via the native import map, though, which keeps it manageable. In contrast to Webpack Module Federation, where the remote map was buried deep in the code.

Conclusion: the friction between three frameworks in one tab is not where you would expect it. Bus and loading ran without problems. Complications arose with the Firefox CSS, the React CJS wrapper, and a Svelte compiler that ran twice in the same page. The code is at github.com/lutzleonhardt/FrankensteinMeetingRoom.

If you have such a landscape in front of you yourself and don't know whether to migrate or federate — write to me on LinkedIn. The problem points that don't come up in standard talks interest me the most.


Lutz Leonhardt is a member of the Native Federation Advisory Board. More at lutzleonhardt.de.