Problem
When Lit elements are used as raw HTML custom element tags in .astro or .mdx files (without client:* directives), the SSR renderer produces DSD output correctly, but hydration never activates on the client. Lit's render() appends duplicate content alongside the existing DSD shadow root content.
Root cause
Two separate issues:
1. before-hydration doesn't fire for raw HTML tags
injectScript('before-hydration', ...) only runs on pages with client:* directives. Pages using raw <my-element> tags with a regular <script> import never trigger it. The hydration support module never loads, so Lit doesn't know to reuse existing DSD shadow roots.
2. Script ordering constraint
globalThis.litElementHydrateSupport is a one-shot callback — LitElement checks for it once during class initialization and ignores it after that. With injectScript('page', ...), Vite may evaluate lit before the callback is set, so it's too late. The setup has to be a synchronous inline <script> in <head>.
Additional issue: defer-hydration elements never initialize
Some SSR'd elements get a defer-hydration attribute. The client.js removes it, but only within Astro's client:* flow. Without client:* directives, nothing removes it, so these elements never start up.
Reproduction
---
import '@my-lit-lib';
---
<html>
<body>
<my-button variant="primary">Click me</my-button>
<script>import '@my-lit-lib';</script>
</body>
</html>
Expected: Single button, interactive after JS loads.
Actual: Button content appears twice in the shadow root.
Fix: #7
Problem
When Lit elements are used as raw HTML custom element tags in
.astroor.mdxfiles (withoutclient:*directives), the SSR renderer produces DSD output correctly, but hydration never activates on the client. Lit'srender()appends duplicate content alongside the existing DSD shadow root content.Root cause
Two separate issues:
1.
before-hydrationdoesn't fire for raw HTML tagsinjectScript('before-hydration', ...)only runs on pages withclient:*directives. Pages using raw<my-element>tags with a regular<script>import never trigger it. The hydration support module never loads, so Lit doesn't know to reuse existing DSD shadow roots.2. Script ordering constraint
globalThis.litElementHydrateSupportis a one-shot callback — LitElement checks for it once during class initialization and ignores it after that. WithinjectScript('page', ...), Vite may evaluatelitbefore the callback is set, so it's too late. The setup has to be a synchronous inline<script>in<head>.Additional issue:
defer-hydrationelements never initializeSome SSR'd elements get a
defer-hydrationattribute. Theclient.jsremoves it, but only within Astro'sclient:*flow. Withoutclient:*directives, nothing removes it, so these elements never start up.Reproduction
Expected: Single button, interactive after JS loads.
Actual: Button content appears twice in the shadow root.
Fix: #7