Use Svelte components within Astro with server-side rendering and client-side hydration
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Client-side hydration system for making server-rendered Svelte components interactive with state management and event handling.
Main client-side renderer that handles component hydration and mounting.
/**
* Creates a client-side renderer for a specific DOM element
* @param element - Target HTML element for component mounting
* @returns Async function that hydrates or mounts components
*/
declare const clientRenderer: (element: HTMLElement) => (
Component: any,
props: Record<string, any>,
slotted: Record<string, any>,
options: Record<string, string>
) => Promise<void>;
export default clientRenderer;The client renderer handles:
Hydrates server-rendered components or mounts new components on the client.
/**
* Hydrates or mounts a Svelte component
* @param Component - Svelte component constructor
* @param props - Component properties
* @param slotted - Slotted content from Astro
* @param options - Hydration options including client directive
* @returns Promise that resolves when component is ready
*/
async function hydrateComponent(
Component: any,
props: Record<string, any>,
slotted: Record<string, any>,
options: { client: string }
): Promise<void>;Client Directive Handling:
The options.client parameter determines the hydration behavior:
"load" - Hydrate immediately"idle" - Hydrate when browser is idle"visible" - Hydrate when component enters viewport"media" - Hydrate based on media query"only" - Mount without hydration (client-only)Creates and manages Svelte component instances with reactive props.
/**
* Creates a managed Svelte component instance
* @param Component - Svelte component constructor
* @param target - Target DOM element
* @param props - Initial component props
* @param shouldHydrate - Whether to hydrate or mount fresh
* @returns Component management interface
*/
function createComponent(
Component: any,
target: HTMLElement,
props: Record<string, any>,
shouldHydrate: boolean
): ComponentInstance;
interface ComponentInstance {
setProps(newProps: Record<string, any>): void;
destroy(): void;
}State Management:
// Props are managed using Svelte 5's $state rune
let propsState = $state(initialProps);
// Props updates are reactive and trigger re-renders
function setProps(newProps: Record<string, any>) {
Object.assign(propsState, newProps);
// Remove props not in newProps
for (const key in propsState) {
if (!(key in newProps)) {
delete propsState[key];
}
}
}Processes Astro slots for client-side rendering with support for both legacy and modern syntax.
interface SlotData {
$$slots?: Record<string, any>;
children?: Snippet;
[key: string]: Snippet | any;
}
type Snippet = import('svelte').Snippet;Slot Transformation:
// Legacy slot support
_$$slots = {
default: true,
[slotName]: createRawSnippet(() => ({
render: () => `<astro-slot name="${slotName}">${content}</astro-slot>`
}))
};
// Modern @render syntax
renderFns = {
children: createRawSnippet(() => ({
render: () => `<astro-slot>${content}</astro-slot>`
})),
[slotName]: createRawSnippet(() => ({
render: () => `<astro-slot name="${slotName}">${content}</astro-slot>`
}))
};Tracks existing component instances to enable prop updates without remounting.
const existingApplications: WeakMap<HTMLElement, ComponentInstance>;Instance Lifecycle:
setProps()createComponent()astro:unmount eventBasic Component Hydration:
// Element with server-rendered Svelte component
const element = document.querySelector('[data-svelte-component]');
const renderer = clientRenderer(element);
await renderer(
MyComponent,
{ title: "Hello", count: 0 },
{ default: "<p>Slot content</p>" },
{ client: "load" }
);Client-Only Component:
await renderer(
ClientOnlyComponent,
{ data: apiData },
{},
{ client: "only" } // Mounts fresh, no hydration
);Prop Updates:
// Initial render
await renderer(Counter, { count: 0 }, {}, { client: "load" });
// Later update (reuses existing instance)
await renderer(Counter, { count: 5 }, {}, { client: "load" });Complex Slots:
await renderer(
LayoutComponent,
{ title: "Page Title" },
{
default: "<main>Main content</main>",
header: "<header>Header</header>",
sidebar: "<aside>Sidebar</aside>"
},
{ client: "visible" }
);Manual Cleanup:
// Components automatically clean up on astro:unmount
element.dispatchEvent(new CustomEvent('astro:unmount'));