Server-Side Rendering (SSR) with Stencil
Stencil provides server-side rendering (SSR) support for React and Vue output targets. If you're using frameworks like Next.js or Nuxt, Stencil automatically enhances these frameworks to render components on the server using a Declarative Shadow DOM.
For detailed setup instructions, refer to the SSR documentation for React and Vue. All interfaces needed for rendering Stencil components into a string are exported through the Stencil Hydrate Module.
Tips & Best Practices
When server-side rendering Stencil components, there are a few potential pitfalls you might encounter. To help you avoid these issues, here are some key tips and best practices.
Avoid Non-Primitive Parameters
When building components, it's common to pass complex data structures like objects to components as props. For example, a footer menu could be structured as an object rather than as separate components for each menu item:
const menu = generateMenuData({ ... })
return (
<nav>
<footer-navigation items={menu} />
</nav>
)
While this approach works fine in the browser, it poses challenges for SSR. While Stencil generally supports the serialization of complex objects within parameters, it can't resolve the value of any function calls, e.g. here generateMenuData(...)
during the SSR process (except when using runtime based SSR within Next.js applications).
We recommend to not depend on external data structures when it comes to rendering Stencil components.
Cross-Component State Handling
When propagating state between parent and child components, patterns like reducers or context providers (as in React) are often used. However, this can be problematic with SSR in frameworks like Next.js, where each component is rendered independently.
Consider the following structure:
<ParentComponent>
<ChildComponent />
</ParentComponent>
When ParentComponent
is rendered on the server, Stencil will attempt to stringify its children (e.g., ChildComponent
) for the light DOM. The intermediate markup may look like this:
<ParentComponent>
<template shadowrootmode="open">
<style>...</style>
...
</template>
<ChildComponent />
</ParentComponent>
At this stage, ParentComponent
can access and manipulate its children. However, when ChildComponent
is rendered in isolation, it won’t have access to the parent’s state or context, potentially leading to inconsistencies.
To prevent this, ensure that components rendered on the server don’t depend on external state or context. If the component relies on data fetched at runtime, it’s better to display a loading placeholder during SSR.
Optimizing Performance
When Stencil server-side renders a component, it converts it into Declarative Shadow DOM, which includes all structural information and styles. While this ensures accurate rendering, it can significantly increase document size if not managed carefully.
For example, consider a button component:
import { Component, Fragment, h } from '@stencil/core'
@Component({
tag: 'my-btn',
styleUrl: './button.css'
})
export class MyBtn {
render() {
return (
<>
<button>...</button>
</>
);
}
}
And this button.css
which imports additional common styles:
/* button.css */
@import "../css/base.css";
@import "../css/tokens.css";
@import "../css/animations.css";
@import "../css/utilities.css";
/* component-specific styles */
button {
...
}
When SSR is performed, the entire CSS (including imports) is bundled with the component’s declarative shadow DOM. Rendering multiple instances of this button in SSR can lead to repeated inclusion of styles, bloating the document size and delaying First Contentful Paint (FCP).
Here are some ways to mitigate this:
- Use CSS Variables: CSS variables can pierce the Shadow DOM, reducing the need for redundant styles.
- Use the
::part
pseudo-element: This allows you to style parts of the Shadow DOM from outside the component, minimizing the internal CSS. - Optimize Component-Specific CSS: Only include the necessary styles for each component.
- Limit SSR Scope: In Next.js, apply
use client
to sections that don’t need SSR to reduce unnecessary rendering.
Stencil continues to enhance SSR capabilities and is committed to solving performance and rendering challenges. Your feedback is important — feel free to file an issue and contribute your ideas!