Skip to content

React TSX dev imports 404 and production build emits invalid CommonJS-style output #2

@mwarger

Description

@mwarger

Summary

I'm trying to use volt 0.7.1 for a small Phoenix-served React page, using a conventional Phoenix-local JS setup under assets/, and React appears to fall off both the dev and production happy paths.

Environment

  • volt 0.7.1
  • Phoenix 1.8.3
  • Elixir 1.19.1
  • React 19.2.3
  • react-dom 19.2.3
  • scheduler 0.27.0

Config shape

config :volt,
  entry: "assets/js/app.tsx",
  outdir: "priv/static/assets",
  target: :es2020,
  sourcemap: :hidden,
  external: ~w(phoenix phoenix_html phoenix_live_view),
  resolve_dirs: ["deps"],
  chunks: %{
    "vendor" => ["react", "react-dom", "scheduler"]
  },
  tailwind: [
    css: "assets/css/app.css",
    sources: [
      %{base: "lib/", pattern: "**/*.{ex,heex}"},
      %{base: "assets/", pattern: "**/*.{ts,tsx,js,jsx,css}"}
    ]
  ]

assets/package.json:

{
  "name": "sportspass-native-assets",
  "private": true,
  "type": "module",
  "dependencies": {
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "scheduler": "0.27.0"
  }
}

I also added a root tsconfig.json in the Phoenix app with the same deps alias approach used by the demo app.

Minimal TSX repro

import React from "react";
import { createRoot } from "react-dom/client";

function MinimalApp(): React.JSX.Element {
  return <h1 data-react-minimal-root>Volt React minimal repro</h1>;
}

const root = document.createElement("div");
document.body.appendChild(root);

createRoot(root).render(
  <React.StrictMode>
    <MinimalApp />
  </React.StrictMode>
);

Dev behavior

The dev server serves the TSX file with bare imports intact:

import React from "react";
import { createRoot } from "react-dom/client";
import { jsx as _jsx } from "react/jsx-runtime";

But the corresponding vendor URL still 404s:

GET /@vendor/react.js -> 404
// vendor module not found: react

So the browser still cannot resolve the React imports in dev.

Production behavior

Running:

mix volt.build --no-hash --entry assets/js/react_minimal.tsx --name react-minimal

prints:

Building "assets/js/react_minimal.tsx"...
  react-minimal-vendor.js  7.4 KB (gzip: 2.8 KB)
  react-minimal-vendor.js  7.4 KB (gzip: 2.8 KB)
  manifest.json  2 entries

The generated manifest is:

{"react-minimal-vendor.js":{"file":"react-minimal-vendor.js","src":"react-minimal-vendor.js"},"react-minimal.js":{"file":"react-minimal-vendor.js","src":"react-minimal.js"}}

There is no actual react-minimal.js output file, only react-minimal-vendor.js.

That emitted react-minimal-vendor.js is also CommonJS-style output starting with:

"use strict";(function(){ ... module.exports=e() })();

In the non-minimal app build, the generated app.js still contains browser-invalid calls like:

var t=require(`scheduler`),n=require(`react`),r=require(`react-dom`);

Expected behavior

I expected one of these to work cleanly for React:

  1. In dev, bare React imports are rewritten to working /@vendor/* URLs.
  2. In production, Volt emits a real browser entry file and browser-valid JS for React rather than CommonJS require(...) calls.

Question

Is React currently meant to be supported by Volt's dev/prod JS pipeline, or is this outside the intended support surface right now?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions