React Workspaces
Description / Purpose
Instead of using the production-style React rendering pipeline (i.e. what you see with create-react-app or Next.js), we have a custom setup for running React workspaces. At the time of writing, this includes react-16 and react-18 workspace types.
Workspace Structure
To build a React workspace, you'll need index.html, index.js, and .babel-adapter.json. The HTML file should import index.compiled.js and our hosted React bundle. Here's an example:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="bundle.css" />
</head>
<body>
<main id="app"></main>
<script src="https://content.codecademy.com/courses/React/react-18-course-bundle.min.js"></script>
<script src="/index.compiled.js"></script>
</body>
</html>
index.js
import React from "react";
import ReactDOM from "react-dom/client";
function App() {
return <h1>Hello from the App component</h1>;
}
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
.babel-adapter.json
{
"excludedModules": ["react", "react-dom"]
}
1. Import the react bundle
The React bundle used in the above index.html is for react-18.
<script src="https://content.codecademy.com/courses/React/react-18-course-bundle.min.js"></script>
If you are building in React 16, you'll use a different bundle: replace react-18 with react-16 in the src attribute.
2. Use an existing DOM node
In index.js, we select an existing DOM node in index.html. In this case it's the element with id app.
ReactDOM.createRoot(document.getElementById("app")).render(<App />);
3. You can import js files
You don't need to write everything in index.js: you can define additional components in separate files and import them. For example, we could have defined the App component in an App.js file and imported it:
import { App } from "./App.js";
4. Use any pre-installed npm package
You can use any npm package available in the Docker image, such as prop-types, react-redux, and msw. The full list of available packages for react-18 is in the ein repository here.
5. Do not include a package.json
For the sake of long-term maintainability, don't do it. If you need additional packages installed, talk to the Learning Platform team.
6. .babel-adapter.json should always have the excludedModules entry
This speeds up compilation and load time by skipping compilation of the react and react-dom packages. Those are pre-compiled and imported via the bundle (see #1).
Note: if you don't want to use index.js or app.js as the entrypoint, you can define one in this file. For example, if your JavaScript entrypoint is main.js, you would have JSON like this (you would have to update index.html to import main.compiled.js as well):
{
"entryFiles": ["main.js"],
"excludedModules": ["react", "react-dom"]
}
7. Import bundle.css (optional)
The react-18 workspace type supports .css imports in JavaScript files. For example, one of your components could have the lines:
import styles from "./style.module.css";
// or
import "./App.css";
If you use this approach, you'll need to import bundle.css in your index.html file. bundle.css will be automatically generated during compilation of your React components:
<link rel="stylesheet" href="bundle.css" />
Behind the scenes
When you have a react-16 or react-18 workspace type and you click Run with any .js file open in the code editor, this script is executed.
- It reads the contents of
.babel-adapter.jsonif it exists - It determines the file to compile:
index.js,app.js, or whatever is defined as the entry file(s) in.babel-adapter.json. - It determines which modules, if any, to exclude from compilation. These are defined in the
excludedModulesarray in.babel-adapter.json. Usually we skipreactandreact-dombecause those are pre-compiled and imported inindex.html. - It compiles the file(s) with browserify and babelify. The output file is something like
index.compiled.jsorapp.compiled.js, depending on your entry file name.
In most cases, this is the effective command that is run in the Docker container:
browserify index.js -o index.compiled.js -t babelify -p [ css-modulesify -o bundle.css ] -t imgurify --exclude react --exclude react-dom
Debugging
Since the compilation process happens in the Docker container, you won't see compilation errors in your browser or console. This is something we would like to fix in the future. For now, the best way to identify compilation errors is by inspecting the websocket connection.
- Use your browser's developer tools and open the network tab.
- Filter for websocket, or WS, request types (if you don't see any websocket requests, reload the page with the network tab open).
- Look for messages with the method
EvalService.Run. - Check for
stderrin the response. - If there is a string there, you have a compilation error. Copy and paste that base64-encoded string into a decoder, e.g. base64decode.org.

Examples
- Initialize State uses the
react-16workspace type