Accord is a contract-first integration layer for Web Component microfrontends. It provides a small host runtime, a Zod-first manifest definition, and type generation so hosts can safely mount remote components.
@accord/manifest: manifest types, validation, JSON schema helpers@accord/host: runtime host for loading and mounting remote components@accord/types: type generator CLI for manifest-driven typings
import { z } from "zod";
import { defineManifest } from "@accord/manifest";
export const manifest = defineManifest({
meta: {
name: "csx-user-card",
version: "1.0.0",
contractVersion: "1.0.0",
tagName: "csx-user-card"
},
props: z.object({
userId: z.string(),
readonly: z.boolean().default(false)
}),
events: {
"user:selected": z.object({ userId: z.string() })
},
capabilities: {
audit: z.object({
log: z.function().args(
z.object({
action: z.string(),
entity: z.string(),
metadata: z.record(z.any()).optional()
})
).returns(z.void())
})
},
hostRequirements: {
styling: "no-shadow-dom",
designTokens: true
}
});accord-types --manifest ./src/csx-user-card.manifest.ts --outDir ./src/generatedThis generates a manifest-types.ts file with typed Props, Events, and Capabilities.
You can also generate types from a hosted manifest.json that was produced using
getManifestJsonSchema in @accord/manifest:
accord-types --manifestUrl https://cdn.example.com/csx-user-card/manifest.json --outDir ./src/generatedBy default the remote workflow emits manifest-types.d.ts. Pass --fileName to override.
React 19 (recommended) — load the remote in <head /> and render the custom
element directly in JSX:
// app/layout.tsx (Next.js example)
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Script
src="https://cdn.example.com/csx-user-card.js"
strategy="beforeInteractive"
/>
</head>
<body>{children}</body>
</html>
);
}// app/page.tsx
export default function Page() {
return <csx-user-card userId="user-123" />;
}The imperative mount API is still useful for non-React hosts, legacy apps, or
cases where you need to dynamically swap containers or wire up host APIs/events
at runtime.
import { mount, registerRemote } from "@accord/host";
registerRemote({
id: "csx-user-card",
url: "https://cdn.example.com/csx-user-card.js",
integrity: "sha384-..."
});
await mount({
remoteId: "csx-user-card",
tagName: "csx-user-card",
container: document.getElementById("slot")!,
props: {
userId: "user-123"
},
hostApi: {
audit: {
log: (event) => console.log("audit", event)
}
},
onEvent: (eventName, payload) => {
console.log("event", eventName, payload);
},
fallback: "Component failed to load",
dev: true
});examples/lit-remote: Lit-based remote component and manifestexamples/next-host: Next.js host application
- Zod is the single source of truth for contracts.
- Hosts pass capabilities through the
hostproperty on custom elements. - The runtime never uses
window.*to inject host services. - The host runtime performs optional dev-time validation of props and event payloads.
- No SSR support for remote components.
- No bundled build pipeline for remotes.
- Contract version negotiation is limited to
semver.satisfies.