Updated
Why a Claude React artifact shows a blank page outside Claude
By João Saraiva
In Claude, the window around a React file does four jobs the file doesn't do itself: it compiles the JSX, supplies the packages the imports name, mounts the component and loads Tailwind. Saved as a file and opened in a browser, or put on a host that serves files as they are, none of that happens, and what you usually get is a white page.
Read the console first
Open the page, then the browser's console. In Chrome on a Mac that is View, then Developer, then JavaScript Console, or Cmd+Option+J; on Windows, Ctrl+Shift+J. The first red line says which of these it is:
Uncaught SyntaxError: Unexpected token '<': the browser can't read JSX.Failed to resolve module specifier "react", orCannot use import statement outside a module: the imports point nowhere.- Nothing at all, and the page is empty: nothing mounts the component.
- Everything is there, but unstyled: Tailwind's classes do nothing.
has been blocked by CORS policy, on afile:///address: the page was opened from your disk.
1. The browser can't read JSX
A line like return <div className="p-8">…</div> is JSX, not JavaScript. Something has to compile it into function calls before a browser runs it. In a plain <script> tag, the browser can't parse the script past the first < it doesn't expect, so none of the script runs. Saved as a .jsx file and opened directly, it doesn't run at all: the browser shows the code as text, or downloads it.
The fix is a compile step: a bundler such as Vite or esbuild, before the page goes anywhere. For a quick test, Babel can compile it in the browser with <script type="text/babel">, which still leaves the imports in the next section to solve.
2. The imports point nowhere
Claude's React files start with lines like these:
import { useState } from "react";
import { LineChart, Line } from "recharts";
import { Card } from "@/components/ui/card";In Claude, the window provides those packages. A browser has no idea what react is. In a module script, a bare name like that is an error until something maps it to an address; in a classic script, import itself is a syntax error. The @/ in the third line is a path alias from a project's configuration, and the Card it names is a shadcn/ui component, a file that doesn't exist outside Claude until you add it.
The fix is a bundler that installs the packages from npm, or an import map that tells the browser where each one is:
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.3.1",
"react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
"react-dom/client": "https://esm.sh/react-dom@18.3.1/client?external=react"
}
}
</script>Every package that uses React has to share the page's one copy of it, which is what ?external=react asks esm.sh for. With two copies, the page fails with Invalid hook call.
3. Nothing mounts the component
Claude's file ends with export default function App() { … }. That defines a component; it doesn't put it on the page. In Claude, the window does that step. On its own, the file defines a function that nothing calls, which isn't an error, so the console stays quiet and the page stays empty.
A page needs an element to render into, and one call that renders it:
<div id="root"></div>
<script type="module">
import { createElement } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.js";
createRoot(document.getElementById("root"))
.render(createElement(App));
</script>Here App.js is the compiled file, not the JSX, and it has to come from a web address (section 5). Older tutorials use ReactDOM.render instead: React 18 deprecated it and React 19 removed it, so on a current React it fails with a TypeError.
4. Tailwind's classes do nothing
Claude styles its React files with Tailwind classes, such as className="p-8 rounded-xl bg-slate-900 text-white". In Claude, the window includes Tailwind's styles. A plain page doesn't, so the classes are only names.
This one is rarely completely blank: the content is there, unstyled, stacked down the left edge. It looks blank when the text was meant to sit on a dark background and is now white on white, or when the layout relies on classes like h-screen and flex to give anything a size.
The fix is Tailwind itself. In a build, it scans your files for class names and writes the CSS they use. For a quick test there is Tailwind's Play CDN, a script that does the same in the browser, which Tailwind's documentation describes as meant for development, not production.
5. The page was opened from your disk
Double-click an HTML file and it opens at a file:/// address. A page there has no web origin of its own, and Chrome and Edge refuse some of what it asks for:
- A module script from another file,
<script type="module" src="app.js">. Module scripts are fetched under CORS rules, which afile:///page can't satisfy. - Babel's
<script type="text/babel" src="App.jsx">, because Babel fetches that file itself and is refused the same way. fetch()of a JSON or CSV file sitting next to the page.
Code written inside the page still runs, and imports from https addresses that allow it, such as esm.sh, still load. The fix is to serve the folder from a local web server and open it at http://localhost on the port the server uses: 3000 for the first of these, 8000 for the second. Run either one in the folder:
npx serve
python3 -m http.serverOr skip all five
Paste the file into Solta, in the box below. When you publish, Solta compiles the JSX, points each import at esm.sh, with a pinned version for the packages these files usually import, serves the shadcn/ui components from its own copy, compiles the Tailwind classes the file uses, and mounts the default export into a page. The page is served at an https address, so the file:/// rules never come into it.
If the file has no export default and doesn't mount itself, Solta won't publish it, and says what to add. If the component throws while it renders, the page shows the error instead of a white page. What it can't fix is a package esm.sh doesn't have, or anything that needs a server: what does not work. A page that asks Claude something while people use it needs that call changed too: how a site calls a model.

