Describe the bug
An input focused before hydration loses focus when a lazy() component is inside Loading. References taken to the server-rendered input and its containing main before hydration still point to the connected nodes afterwards, and the typed value is preserved, but the connected main is passed to insertBefore again. Hydration finishes and the counter works. Removing Loading, or replacing the lazy component with the static component, preserves focus.
Your Example Website or App
https://github.com/HitoshiroKyoka/solid2-rc13-loading-repros/tree/main/repro-a
Steps to Reproduce the Bug or Issue
- Run
npx vite --host 127.0.0.1 --port 4100 --strictPort.
- Open
http://127.0.0.1:4100/. Within five seconds, focus the SSR input and start typing.
- Keep typing as hydration finishes: the input loses focus and stops receiving text. Clicking the counter afterwards changes it to
Count: 1.
- Remove
<Loading> from App.tsx (return <Page />), or replace the lazy declaration with import Page from './Page';. Reload and repeat: focus stays in the input.
Expected behavior
Hydration should preserve the focused SSR input when adopting the same DOM nodes.
Screenshots or Videos
No response
Platform
Linux (Debian 13), Node 24.18.0; Chromium 153.0.8010.12. solid-js / @solidjs/web 2.0.0-rc.13, @solidjs/vite-plugin 3.0.0-next.47, Vite 8.3.2; development SSR.
Additional context
The repository also has ?mode=no-loading and ?mode=no-lazy controls and an automated check. A trimmed version with only the failing case is inline below.
In an empty directory, use Node 22.12+ and run:
npm init -y && npm pkg set type=module
npm i solid-js@2.0.0-rc.13 @solidjs/web@2.0.0-rc.13 && npm i -D @solidjs/vite-plugin@3.0.0-next.47 vite@8.3.2
mkdir src
Write these files:
vite.config.ts
import { defineConfig } from 'vite';
import solid from '@solidjs/vite-plugin';
export default defineConfig({ plugins: [
solid({ ssr: true, start: { renderMode: 'async' } }),
{ name: 'delay-hydration', configureServer(server) {
server.middlewares.use((req, _res, next) => {
// Give the user time to focus the server-rendered input.
if (req.url?.includes('solid-ssr-entry-client')) setTimeout(next, 5000);
else next();
});
} },
] });
src/App.tsx
import { lazy, Loading } from 'solid-js';
const Page = lazy(() => import('./Page'), undefined, 'src/Page.tsx');
export default function App() {
return <Loading fallback="Loading..."><Page /></Loading>;
}
src/Page.tsx
import { createSignal } from 'solid-js';
export default function Page() {
const [count, setCount] = createSignal(0);
return <><header>Header</header><main><input aria-label="Type before hydration" placeholder="Type here within 5 seconds" /><button onClick={() => setCount(count() + 1)}>Count: {count()}</button></main></>;
}
I reproduced this locally by following the steps above. The investigation and the reduction were done with AI assistance (Claude Code and Codex); I checked the reproduction and the control case by hand before posting.
Describe the bug
An input focused before hydration loses focus when a
lazy()component is insideLoading. References taken to the server-rendered input and its containingmainbefore hydration still point to the connected nodes afterwards, and the typed value is preserved, but the connectedmainis passed toinsertBeforeagain. Hydration finishes and the counter works. RemovingLoading, or replacing the lazy component with the static component, preserves focus.Your Example Website or App
https://github.com/HitoshiroKyoka/solid2-rc13-loading-repros/tree/main/repro-a
Steps to Reproduce the Bug or Issue
npx vite --host 127.0.0.1 --port 4100 --strictPort.http://127.0.0.1:4100/. Within five seconds, focus the SSR input and start typing.Count: 1.<Loading>fromApp.tsx(return<Page />), or replace the lazy declaration withimport Page from './Page';. Reload and repeat: focus stays in the input.Expected behavior
Hydration should preserve the focused SSR input when adopting the same DOM nodes.
Screenshots or Videos
No response
Platform
Linux (Debian 13), Node 24.18.0; Chromium 153.0.8010.12.
solid-js/@solidjs/web2.0.0-rc.13,@solidjs/vite-plugin3.0.0-next.47, Vite 8.3.2; development SSR.Additional context
The repository also has
?mode=no-loadingand?mode=no-lazycontrols and an automated check. A trimmed version with only the failing case is inline below.In an empty directory, use Node 22.12+ and run:
Write these files:
vite.config.tssrc/App.tsxsrc/Page.tsxI reproduced this locally by following the steps above. The investigation and the reduction were done with AI assistance (Claude Code and Codex); I checked the reproduction and the control case by hand before posting.