diff --git a/cli/src/guidance/skills.gen.ts b/cli/src/guidance/skills.gen.ts
index 298db50ca4..d04affa93a 100644
--- a/cli/src/guidance/skills.gen.ts
+++ b/cli/src/guidance/skills.gen.ts
@@ -5585,7 +5585,21 @@ A raw app has three logical parts:
### Entrypoint
-\`index.tsx\` is the bundling entrypoint. It typically renders a top-level \`App\` component. The bundler is esbuild.
+\`index.tsx\` is the bundling **and mount** entrypoint (esbuild bundles it; the preview then executes it against an empty \`
\`). It MUST itself mount a top-level \`App\` component into \`#root\` — nothing is auto-rendered for you. Keep your UI in \`App.tsx\` (or \`App.svelte\` / \`App.vue\`) and keep \`index.tsx\` as the mount shim.
+
+React:
+
+\`\`\`tsx
+import React from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+
+createRoot(document.getElementById('root')!).render(
)
+\`\`\`
+
+Svelte: \`mount(App, { target: document.getElementById('root')! })\`. Vue: \`createApp(App).mount('#root')\`.
+
+**Never turn \`index.tsx\` into a bare component** (e.g. \`export default function App() { ... }\` with no \`createRoot(...).render(...)\`). A component that is defined but never mounted renders **a blank screen with NO error thrown** — the JSX never executes, so nothing surfaces in the console or the error overlay. If the app is blank, the first thing to check is that \`index.tsx\` actually calls \`createRoot(document.getElementById('root')!).render(
)\`.
**Always begin every React file (\`.tsx\`/\`.jsx\`) that uses JSX with \`import React from 'react'\`.** esbuild uses the classic JSX transform, so \`React\` must be in scope wherever JSX appears — a missing import compiles fine but throws \`React is not defined\` at runtime, leaving a blank screen.
diff --git a/frontend/src/lib/components/copilot/chat/app/core.ts b/frontend/src/lib/components/copilot/chat/app/core.ts
index 599a77e906..872957bd5a 100644
--- a/frontend/src/lib/components/copilot/chat/app/core.ts
+++ b/frontend/src/lib/components/copilot/chat/app/core.ts
@@ -925,6 +925,16 @@ export function prepareAppSystemMessage(customPrompt?: string): ChatCompletionSy
### Frontend
- The frontend is bundled using esbuild with entrypoint \`index.tsx\`
+- \`index.tsx\` is also the **mount** entrypoint: it MUST mount a top-level \`App\` into \`#root\` — nothing is auto-rendered. Keep your UI in \`App.tsx\` and keep \`index.tsx\` as the mount shim:
+ \`\`\`tsx
+ import React from 'react'
+ import { createRoot } from 'react-dom/client'
+ import App from './App'
+
+ createRoot(document.getElementById('root')!).render(
)
+ \`\`\`
+ (Svelte: \`mount(App, { target: document.getElementById('root')! })\`; Vue: \`createApp(App).mount('#root')\`.)
+- **Never replace \`index.tsx\` with a bare component.** A component that is defined but never mounted produces a **blank screen with NO error** (the JSX never runs, so nothing throws). If the app renders blank, first verify \`index.tsx\` calls \`createRoot(document.getElementById('root')!).render(
)\`.
- Frontend files are managed separately from backend runnables
- The \`wmill.d.ts\` file is generated automatically from the backend runnables shape
- Begin every React file (\`.tsx\`/\`.jsx\`) that uses JSX with \`import React from 'react'\`. Raw apps bundle with the classic JSX transform, so \`React\` must be in scope wherever JSX is used — a missing import compiles fine but throws \`React is not defined\` at runtime.
diff --git a/system_prompts/auto-generated/prompts.ts b/system_prompts/auto-generated/prompts.ts
index 16ee0616bc..7cab373fcd 100644
--- a/system_prompts/auto-generated/prompts.ts
+++ b/system_prompts/auto-generated/prompts.ts
@@ -611,7 +611,21 @@ A raw app has three logical parts:
### Entrypoint
-\`index.tsx\` is the bundling entrypoint. It typically renders a top-level \`App\` component. The bundler is esbuild.
+\`index.tsx\` is the bundling **and mount** entrypoint (esbuild bundles it; the preview then executes it against an empty \`
\`). It MUST itself mount a top-level \`App\` component into \`#root\` — nothing is auto-rendered for you. Keep your UI in \`App.tsx\` (or \`App.svelte\` / \`App.vue\`) and keep \`index.tsx\` as the mount shim.
+
+React:
+
+\`\`\`tsx
+import React from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+
+createRoot(document.getElementById('root')!).render(
)
+\`\`\`
+
+Svelte: \`mount(App, { target: document.getElementById('root')! })\`. Vue: \`createApp(App).mount('#root')\`.
+
+**Never turn \`index.tsx\` into a bare component** (e.g. \`export default function App() { ... }\` with no \`createRoot(...).render(...)\`). A component that is defined but never mounted renders **a blank screen with NO error thrown** — the JSX never executes, so nothing surfaces in the console or the error overlay. If the app is blank, the first thing to check is that \`index.tsx\` actually calls \`createRoot(document.getElementById('root')!).render(
)\`.
**Always begin every React file (\`.tsx\`/\`.jsx\`) that uses JSX with \`import React from 'react'\`.** esbuild uses the classic JSX transform, so \`React\` must be in scope wherever JSX appears — a missing import compiles fine but throws \`React is not defined\` at runtime, leaving a blank screen.
diff --git a/system_prompts/auto-generated/skills/raw-app/SKILL.md b/system_prompts/auto-generated/skills/raw-app/SKILL.md
index 624fdc97ac..f95358a655 100644
--- a/system_prompts/auto-generated/skills/raw-app/SKILL.md
+++ b/system_prompts/auto-generated/skills/raw-app/SKILL.md
@@ -249,7 +249,21 @@ A raw app has three logical parts:
### Entrypoint
-`index.tsx` is the bundling entrypoint. It typically renders a top-level `App` component. The bundler is esbuild.
+`index.tsx` is the bundling **and mount** entrypoint (esbuild bundles it; the preview then executes it against an empty `
`). It MUST itself mount a top-level `App` component into `#root` — nothing is auto-rendered for you. Keep your UI in `App.tsx` (or `App.svelte` / `App.vue`) and keep `index.tsx` as the mount shim.
+
+React:
+
+```tsx
+import React from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+
+createRoot(document.getElementById('root')!).render(
)
+```
+
+Svelte: `mount(App, { target: document.getElementById('root')! })`. Vue: `createApp(App).mount('#root')`.
+
+**Never turn `index.tsx` into a bare component** (e.g. `export default function App() { ... }` with no `createRoot(...).render(...)`). A component that is defined but never mounted renders **a blank screen with NO error thrown** — the JSX never executes, so nothing surfaces in the console or the error overlay. If the app is blank, the first thing to check is that `index.tsx` actually calls `createRoot(document.getElementById('root')!).render(
)`.
**Always begin every React file (`.tsx`/`.jsx`) that uses JSX with `import React from 'react'`.** esbuild uses the classic JSX transform, so `React` must be in scope wherever JSX appears — a missing import compiles fine but throws `React is not defined` at runtime, leaving a blank screen.
diff --git a/system_prompts/base/raw-app.md b/system_prompts/base/raw-app.md
index 004302cc74..a3c06538b3 100644
--- a/system_prompts/base/raw-app.md
+++ b/system_prompts/base/raw-app.md
@@ -14,7 +14,21 @@ A raw app has three logical parts:
### Entrypoint
-`index.tsx` is the bundling entrypoint. It typically renders a top-level `App` component. The bundler is esbuild.
+`index.tsx` is the bundling **and mount** entrypoint (esbuild bundles it; the preview then executes it against an empty `
`). It MUST itself mount a top-level `App` component into `#root` — nothing is auto-rendered for you. Keep your UI in `App.tsx` (or `App.svelte` / `App.vue`) and keep `index.tsx` as the mount shim.
+
+React:
+
+```tsx
+import React from 'react'
+import { createRoot } from 'react-dom/client'
+import App from './App'
+
+createRoot(document.getElementById('root')!).render(
)
+```
+
+Svelte: `mount(App, { target: document.getElementById('root')! })`. Vue: `createApp(App).mount('#root')`.
+
+**Never turn `index.tsx` into a bare component** (e.g. `export default function App() { ... }` with no `createRoot(...).render(...)`). A component that is defined but never mounted renders **a blank screen with NO error thrown** — the JSX never executes, so nothing surfaces in the console or the error overlay. If the app is blank, the first thing to check is that `index.tsx` actually calls `createRoot(document.getElementById('root')!).render(
)`.
**Always begin every React file (`.tsx`/`.jsx`) that uses JSX with `import React from 'react'`.** esbuild uses the classic JSX transform, so `React` must be in scope wherever JSX appears — a missing import compiles fine but throws `React is not defined` at runtime, leaving a blank screen.