Strata architecture and SDDMenu
On this page

External Angular package islands

Status: locally qualified implementation candidate on the feature branch; hosted CI and release verification are pending. Starting main SHA: 88f68832361694caf683d529adc15d5e628559ea.

Public configuration

strataServerComponents accepts clientComponents, an array of explicit registrations:

clientComponents: [
  {
    selector: "acme-product-picker",
    module: "@acme/angular-widgets",
    exportName: "ProductPickerComponent",
  },
];

module is a bare package specifier, including an optional package subpath. exportName is a named export. Selectors must be custom element names. Missing fields, getters, extra keys, invalid values and duplicate selectors fail synchronously when the plugin is configured, before graph generation. Aliases such as ProductPickerComponent as Picker work because the analyzer matches the imported export name, not the local binding name. Default and namespace imports are not supported.

@acme/angular-widgets and ProductPickerComponent above are placeholders. Consumers must replace them with an installed package and its actual public export. The runnable test fixture uses the private tarball @strata-test/angular-widgets and is never published.

Resolution and Angular compilation

The analyzer walks only app-owned component metadata. A package reference becomes a client reference only when all three inputs match: the element has [strataClient], the server-owned component lists the imported package symbol in its Angular imports, and that import's package plus exported name matches one registry entry for the element selector. The component class is not parsed or crawled. Unmarked package components stay opaque. Local app components keep their existing relative-import resolution and are checked first for selector conflicts.

The browser surrogate imports the exact named export from the bare package specifier. Vite includes registered package entry points in dev dependency prebundling and bundles them in the SSR graph so Angular's linker can process partial declarations. Angular AOT then sees the same library component in the server-owned template and the generated surrogate's provider. No JIT compiler, private Ivy API or Strata runtime import is added to the package.

The test-only library is independently compiled with Angular's public ngc compiler using compilationMode: "partial", then packed as a private tarball using an Angular Package Format dist/fesm2022 entry point. This path lets Analog's Angular linker process partial declarations in the SSR bundle. An initial non-APF fixture exposed the JIT fallback; moving the entry into the normal FESM path made the linked production consumer pass. The isolated npm consumer uses a peer-compatible Angular 22.2.3 set, @angular/build 22.1.8, Analog Router 2.8.1 and Vite 8.3.0.

Boundary data and security

Package components use the existing protocol. Primitive-only root props remain v1. Nested view models use v2, with unchanged limits: 64 root props, 8 nested containers, 512 values, 128 array elements, 64 keys per object and 64 KiB serialized JSON per boundary. Runtime input reflection uses Angular's public component mirror and checks the public (aliased) input name before assignment. Each boundary keeps its own validated data snapshot.

The package is not an automatic trust grant. The usual browser graph firewall remains active for server-owned components and explicitly marked server-only modules. A package that imports Strata's server-only assertion is rejected if that dependency enters the browser graph. Browser-bundled packages must otherwise be independently safe for browser use; this feature does not scan every file in node_modules. Never include repositories, secrets or private values in the boundary DTO.

Development behavior

Consumer app edits regenerate the same transactional graph as before. A registry change requires a Vite restart because the plugin options are read at startup. Installed package source is not watched; rebuild/reinstall the package and restart the dev server after changing it. Failed graph analysis continues to close the client graph until the app source is valid again. Existing server-owned edit reload behavior is unchanged.

Qualification status

CheckResult on this branch
Analyzer, registry validation, local references and package surrogate unit tests257 Server Components tests pass
Package declarations, package build and repository build/typecheck/lint/format/testPass
Independent fixture partial compilation and packPass; Angular partial declarations in FESM entry
Isolated npm install and TypeScript 5.9/6 declaration portabilityPass
Consumer Angular AOT, Node SSR, v1/v2 and Chromium hydration/interactionPass; DOM identity, aliased structured input and sibling-state isolation
Installed-plugin server-only firewall and graph confidentialityPass
Vite dev, package-island hydration and server-owned edit reloadPass
Cloudflare Pages build and local workerd SSR/hydrationPass
Browser/SSR bundle size comparisonNot measured
Hosted CI and published registry verificationPending; branch has not been pushed and nothing is published

The qualification runner passed with zero failures. It verifies the independent tarballs are installed outside the workspace, one Angular runtime is present, browser maps and bundles exclude server implementation files, and the package component hydrates the server-rendered DOM. The feature remains a candidate until hosted CI completes; no production app adoption or release is claimed.

Limits and migration

Registration is opt-in and exact. Re-export barrels, namespace imports, default imports, package source crawling, global component registries and automatic island discovery remain unsupported. Angular AOT is expected to reject an export that is not a standalone component or whose actual selector does not match the element. Those negative package-export cases are not part of the passing positive consumer fixture. The qualified peer ranges remain unchanged.

Existing local-component configurations need no migration. To try a package component, install a publicly exported standalone library, add one registry entry, import that same named export into the Server Component's imports, and send a public DTO through [strataClient]. Restart Vite after changing the registration or installed package.

Source: docs/research/server-component-package-islands.md

Generated by Entwine · Markdown remains the source of truth.