diff --git a/cspell-wordlist.txt b/cspell-wordlist.txt
index 090262807..9770f70d9 100644
--- a/cspell-wordlist.txt
+++ b/cspell-wordlist.txt
@@ -20,6 +20,7 @@ expressjs
lifecycles
microtask
minifier
+Nuxt
polyfilling
precache
prehydration
diff --git a/docs/framework-integration/react.md b/docs/framework-integration/react.md
index c805c541c..6d087626a 100644
--- a/docs/framework-integration/react.md
+++ b/docs/framework-integration/react.md
@@ -16,6 +16,7 @@ This package includes an output target for code generation that allows developer
- ♻️ Automate the generation of React component wrappers for Stencil components
- 🌐 Generate React functional component wrappers with JSX bindings for custom events and properties
- ⌨️ Typings and auto-completion for React components in your IDE
+- 🚀 Support for Server Side Rendering (SSR) when used with frameworks like [Next.js](https://nextjs.org/)
To generate these framework wrappers, Stencil provides an Output Target library called [`@stencil/react-output-target`](https://www.npmjs.com/package/@stencil/react-output-target) that can be added to your `stencil.config.ts` file. This also enables Stencil components to be used within e.g. Next.js or other React based application frameworks.
@@ -129,10 +130,11 @@ In your `react-library` project, create a project specific `tsconfig.json` that
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
- "lib": ["dom", "es2015"],
- "module": "esnext",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
"moduleResolution": "bundler",
- "target": "es2015",
"skipLibCheck": true,
"jsx": "react",
"allowSyntheticDefaultImports": true,
@@ -365,6 +367,68 @@ function App() {
export default App;
```
+### Enable Server Side Rendering (SSR)
+
+If your React framework supports server side rendering, e.g. [Next.js](https://nextjs.org/) your Stencil components will get automatically server side rendered, if set up correctly. In order to enable this:
+
+1. Add a `dist-hydrate-script` output target to your `stencil.config.ts` if not already existing, e.g.:
+ ```ts title="stencil.config.ts"
+ import { Config } from '@stencil/core';
+
+ export const config: Config = {
+ outputTargets: [
+ {
+ type: 'dist-hydrate-script',
+ dir: './hydrate',
+ },
+ // ...
+ ]
+ };
+ ```
+
+2. Create an export for the compiled files within the `/hydrate` directory, e.g.
+ ```json title="package.json"
+ {
+ "name": "component-library",
+ ...
+ "exports": {
+ ...
+ "./hydrate": {
+ "types": "./hydrate/index.d.ts",
+ "import": "./hydrate/index.js",
+ "require": "./hydrate/index.cjs.js",
+ "default": "./hydrate/index.js"
+ },
+ ...
+ },
+ ...
+ }
+ ```
+
+3. Set the `hydrateModule` in your React output target configuration, e.g.
+ ```ts title="stencil.config.ts"
+ import { Config } from '@stencil/core';
+ import { reactOutputTarget } from '@stencil/react-output-target';
+
+ export const config: Config = {
+ outputTargets: [
+ reactOutputTarget({
+ outDir: '../react-library/lib/components/stencil-generated/',
+ hydrateModule: 'component-library/hydrate'
+ }),
+ // ...
+ ]
+ };
+ ```
+
+That's it! Your Next.js application should now render a Declarative Shadow DOM on the server side which will get automatically hydrated once the React runtime initiates.
+
+:::cautions
+
+A Declarative Shadow DOM not only encapsulates the HTML structure of a component but also includes all associated CSS. When server-side rendering numerous small components with extensive CSS, the overall document size can significantly increase, leading to longer initial page load times. To optimize performance, it's essential to maintain a manageable document size that aligns with your performance objectives. It is advisable to server-side render only the critical components required for rendering the initial viewport, while deferring the loading of additional components until after the initial render.
+
+:::
+
## API
### esModule
diff --git a/docs/framework-integration/vue.md b/docs/framework-integration/vue.md
index 143a858a8..86410de7c 100644
--- a/docs/framework-integration/vue.md
+++ b/docs/framework-integration/vue.md
@@ -348,6 +348,72 @@ In your page or component, you can now import and use your component wrappers:
```
+### Enable Server Side Rendering (SSR)
+
+If your Vue framework supports server side rendering, e.g. when using [Nuxt](https://nuxt.com/) your Stencil components will get automatically server side rendered, if set up correctly. In order to enable this:
+
+1. Add a `dist-hydrate-script` output target to your `stencil.config.ts` if not already existing, e.g.:
+ ```ts title="stencil.config.ts"
+ import { Config } from '@stencil/core';
+
+ export const config: Config = {
+ outputTargets: [
+ {
+ type: 'dist-hydrate-script',
+ dir: './hydrate',
+ },
+ // ...
+ ]
+ };
+ ```
+
+2. Create an export for the compiled files within the `/hydrate` directory, e.g.
+ ```json title="package.json"
+ {
+ "name": "component-library",
+ ...
+ "exports": {
+ ...
+ "./hydrate": {
+ "types": "./hydrate/index.d.ts",
+ "import": "./hydrate/index.js",
+ "require": "./hydrate/index.cjs.js",
+ "default": "./hydrate/index.js"
+ },
+ ...
+ },
+ ...
+ }
+ ```
+
+3. Set the `hydrateModule` in your React output target configuration, e.g.
+ ```ts title="stencil.config.ts"
+ import { Config } from '@stencil/core';
+ import { vueOutputTarget } from '@stencil/vue-output-target';
+
+ export const config: Config = {
+ outputTargets: [
+ vueOutputTarget({
+ includeImportCustomElements: true,
+ includePolyfills: false,
+ includeDefineCustomElements: false,
+ componentCorePackage: 'component-library',
+ hydrateModule: 'component-library/hydrate',
+ proxiesFile: '../component-library-vue/src/index.ts',
+ }),
+ // ...
+ ]
+ };
+ ```
+
+That's it! Your Nuxt application should now render a Declarative Shadow DOM on the server side which will get automatically hydrated once the Vue runtime initiates.
+
+:::cautions
+
+A Declarative Shadow DOM not only encapsulates the HTML structure of a component but also includes all associated CSS. When server-side rendering numerous small components with extensive CSS, the overall document size can significantly increase, leading to longer initial page load times. To optimize performance, it's essential to maintain a manageable document size that aligns with your performance objectives. It is advisable to server-side render only the critical components required for rendering the initial viewport, while deferring the loading of additional components until after the initial render.
+
+:::
+
## API
### componentCorePackage
diff --git a/docs/guides/server-side-rendering.md b/docs/guides/server-side-rendering.md
new file mode 100644
index 000000000..11188aca8
--- /dev/null
+++ b/docs/guides/server-side-rendering.md
@@ -0,0 +1,138 @@
+---
+title: Server Side Rendering
+sidebar_label: Server Side Rendering
+description: Server Side Rendering
+slug: /server-side-rendering
+---
+
+# 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](https://nextjs.org/) or [Nuxt](https://nuxt.com/), Stencil automatically enhances these frameworks to render components on the server using a [Declarative Shadow DOM](https://web.dev/articles/declarative-shadow-dom).
+
+For detailed setup instructions, refer to the SSR documentation for [React](/docs/react) and [Vue](/docs/vue). All interfaces needed for rendering Stencil components into a string are exported through the [Stencil Hydrate Module](/docs/hydrate-app).
+
+## 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:
+
+```tsx
+const menu = {
+ 'Overview': ['Introduction', 'Getting Started', 'Component API', 'Guides', 'FAQ'],
+ 'Docs': ['Framework Integrations', 'Static Site Generation', 'Config', 'Output Targets', 'Testing', 'Core Compiler API'],
+ 'Community': ['Blog', 'GitHub', 'X', 'Discord']
+}
+return (
+
+)
+```
+
+While this approach works fine in the browser, it poses challenges for SSR. Stencil **does not support** the serialization of complex objects within parameters, so the footer items may not render on the server.
+
+A better approach is to structure dynamic content as part of the component's light DOM rather than passing it as props. This ensures that the framework can fully render the component during SSR, avoiding hydration issues. Here’s an improved version of the example:
+
+```tsx
+const menu = {
+ 'Overview': ['Introduction', 'Getting Started', 'Component API', 'Guides', 'FAQ'],
+ 'Docs': ['Framework Integrations', 'Static Site Generation', 'Config', 'Output Targets', 'Testing', 'Core Compiler API'],
+ 'Community': ['Blog', 'GitHub', 'X', 'Discord']
+}
+return (
+
+)
+```
+
+By rendering the menu directly in the light DOM, SSR can produce a complete, ready-to-render markup.
+
+### 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:
+
+```tsx
+
+
+
+```
+
+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:
+
+```tsx
+
+
+
+ ...
+
+
+
+```
+
+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](https://web.dev/articles/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:
+
+```tsx
+import { Component, Fragment, h } from '@stencil/core'
+@Component({
+ tag: 'my-btn',
+ styleUrl: './button.css'
+})
+export class MyBtn {
+ render() {
+ return (
+ <>
+
+ >
+ );
+ }
+}
+```
+
+And this `button.css` which imports additional common styles:
+
+```css
+/* 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)](https://web.dev/articles/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](https://github.com/ionic-team/stencil/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=feat%3A+) and contribute your ideas!