bootfactory— only the service map differs클라이언트와 서버가 같은bootfactory를 호출 — 서비스 맵만 다름getServerSideProps, no client/server boundary컴포넌트는 어디서 도는지 모름:getServerSideProps도 클라이언트/서버 경계도 없음@propertyfields are hydrated through the custom-element upgrade algorithm — no re-fetch@property필드는 커스텀 엘리먼트 업그레이드로 하이드레이션 — 재조회 없음@dooboostore/simple-boot-http-server-ssrprovides a powerfulServer-Side Rendering (SSR)environment by seamlessly integrating@dooboostore/simple-boot-frontand@dooboostore/simple-boot-http-server.
It shipsfive interchangeable rendering strategiesas separateFilters, so you pick the one that fits your app instead of being locked into one DOM backend:
| Filter | DOM backend | Instance reuse | Best for |
|---|---|---|---|
SSRFilter | jsdom | Pooled (min/max, generation-based eviction) | High-traffic apps that want warm instances ready to render |
SSRDomParserFilter | @dooboostore/dom-parser(this monorepo's own lightweight, dependency-free DOM) | Fresh window per request | Lower memory footprint, no jsdom dependency; pairs with the AOP-based data-hydration proxy below |
SSRLinkDomFilter | linkedom | Pooled (poolOptionmin/max, generation-based eviction) | Alternative lightweight DOM backend |
SSRSimpleWebComponentDomParserFilter | @dooboostore/dom-parser | Fresh window per request | @dooboostore/simple-web-componentapps without a browser;intentServicesinjection and SWC@propertyhydration (below) |
SSRSimpleWebComponentFilter | Real headlessChromium via Playwright | New browser context per request (browser instance itself is reused) | Pixel/spec-perfect rendering (declarative Shadow DOM, real browser APIs) for@dooboostore/simple-web-componentapps, at the cost of spinning up a real browser |
All of them execute your existing frontend code (routing, components, services) on the server and return fully rendered HTML — no separate server-only rendering path to maintain.
jsdom, this monorepo's own@dooboostore/dom-parser,linkedom, or a real headless Chromium (Playwright) — swap theFilterwithout touching your app code.SSRFilter): Optimize resource management by poolingjsdom-backed SSR instances (withmin/maxpool size and generation-based stale eviction) to handle concurrent requests efficiently.SSRDomParserFilterDataHydrationProxytransparently wraps@Sim-registered service methods — on the server it caches the method's result keyed bysymbol.method(args), embeds it in the HTML, and on the client it's read back instead of re-fetching, so hydration requires no manual wiring in your services.SSRSimpleWebComponentFilterdrives a real Chromium instance via Playwright so@dooboostore/simple-web-componentapps (declarative Shadow DOM, native Custom Elements) render exactly as they would in a real browser.pnpm add @dooboostore/simple-boot-http-server-ssr reflect-metadata# jsdom, linkedom, and playwright are regular dependencies; for SSRSimpleWebComponentFilter also run:npx playwright install chromiumDefine a shared function to initialize the SWC app on both server and client.
importswcRegister, {typeSwcAppInterface}from'@dooboostore/simple-web-component';exportdefaultasync(window:Window,otherInstanceSim?:Map<symbol,any>,urlPath?:string) => {// Register the built-in SWC elements (swc-app, ...) on this windowawaitswcRegister(window);constapp =window.document.querySelector('#app')asSwcAppInterface;awaitapp.connect({window,path: urlPath ??'/',routeType:'path',
otherInstanceSim,// e.g. the `sim` Map from registerComponentsonStartedLazyDefineComponent: [/* component/page factories */]
});return{ app };
};Add one of the SSR filters to your server configuration. Example usingSSRSimpleWebComponentDomParserFilter:
constssrFilter =newSSRSimpleWebComponentDomParserFilter({frontDistPath:'./dist-client',frontDistIndexFileName:'index.html',// Services exposed to SSR: each request gets them with its own `rr` injected, passed as `sim`intentServices: [/* SimConfig items with a symbol */],registerComponents:async(window:any,rr:RequestResponse,sim:Map<symbol,any>) => {const{ app } =awaitbootfactory(window, sim,UrlUtils.getUrlPath(window.location));returnapp;// returning the SwcAppInterface enables @property hydration},ssrExcludeFilter:rr=>/\.(js|css|map|ico|png|json)$/.test(rr.reqUrlPathName)
});constserver =newSimpleBootHttpSSRServer(newHttpSSRServerOption({listen: {port:8080},filters: [newResourceFilter('./dist-client', ['/bundle.js',/\.map$/]), ssrFilter]
})
);
server.run();SSRSimpleWebComponentFilter(real Chromium via Playwright) takes the samefrontDistPath/frontDistIndexFileName/ssrExcludeFilterplusplaywright: { waitUntil, waitForSelector, waitForTimeout, timeout, ignoreTags }. Playwright loads your real bundle, so Custom Elements register themselves andregisterComponentsis usually unnecessary.
For@dooboostore/simple-boot-frontapps, useSSRDomParserFilter(orSSRFilterforjsdom/SSRLinkDomFilterforlinkedom, both pooled viapoolOption: { min, max, clearIntervalTime? }) — sameFilterinterface:
constdomParserFilter =newSSRDomParserFilter({frontDistPath:'./dist-client',frontDistIndexFileName:'index.html',factorySimFrontOption:(window) =>newSimFrontOption({window/* ... */}),factory: mySimpleBootHttpSSRFactory,using: [/* components/pages to register */]
});constserver =newSimpleBootHttpSSRServer(newHttpSSRServerOption({filters: [newResourceFilter('./dist-client'), domParserFilter]
})
);
server.run();SSRDomParserFilterpairs withSSRDomParserFilterDataHydrationProxy(@Sim-injected, wraps@Sim-registered service methods) to automate transferring async data from server to client with no manual wiring in your services:
simpleBootFront.saveDataHydration(key, data)(key=<service symbol>.<method>(<args>)) and serialized into the HTML.simpleBootFront.cutDataHydration(key)returns the cached value instead of re-running the (async) call.SSRFilter/SSRWorker(thejsdom-based filters) use a simpler, manual mechanism instead: whatever you assign ontowindow.server_side_dataon the server is serialized as an inline<script>and available onwindow.server_side_dataon the client.
@propertyhydration (SSRSimpleWebComponentDomParserFilter)registerComponentsreturns theSwcAppInterface(host +connectedElements()).@propertymetadata is collected from the host and connected elements into{ sel, prop, value }items (sel=[swc-use-ssr="<id>"], JSON-serializable values only).<script>that runs before the bundle: finds each node withquerySelectorand setsel[prop] = value.<→\u003c, U+2028/U+2029), so a value
containing</script>can't break out of the tag.JSON.parserestores it exactly.customElements.define—
the upgrade doesn't reset them. Declare hydrated fields withdeclare(see the SWC README).Check out the detailed guides and tutorials in thedocumentfolder.