Issue #941

In this tutorial we will be creating a React app with Parcel 2 with Typescript and Tailwind

Install the following dependencies. Parcel supports TypeScript out of the box without any additional configuration.

npm install --save-dev parcel

npm install react react-dom
npm install @types/react @types/react-dom --dev

npm install -D tailwindcss postcss
npx tailwindcss init

We will be adding files to src folder

src
   - index.html
   - App.tsx
   - App.css

Create a .postcssrc with the following

{
  "plugins": {
    "tailwindcss": {}
  }
}

Supposed we are adding files in src folder, allow Tailwind to act in the following content paths in tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{html,js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

In our index.html, add a placeholder div to inject later with script

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>My Parcel App</title>
    </head>
    <body>
        <div id="app"></div>
        <script type="module" src="index.tsx"></script>
    </body>
</html>

Here is how we inject our React component

import { createRoot } from "react-dom/client"
import App from "./App"

const container = document.getElementById("app")!
const root = createRoot(container)
root.render(<App />);

Our App component can be simple like this

import React from "react"
import "./App.css"

function App() {
    return (
        <div className="App">
            <header className="App-header">
                <a>
                    Learn React
                </a>
            </header>
            <h1 className="text-5xl font-italic underline text-green-400">Hello world!</h1>
        </div>
    )
}

export default App

Specify Tailwind layers in App.css

@tailwind base;
@tailwind components;
@tailwind utilities;

We will be adding 2 handy scripts to our package.json, specifying src/index.html as our starting point

{
    "name": "parcel app",
    "version": "1.0.0",
    "description": "",
    "source": "src/index.html",
    "scripts": {
        "start": "parcel --open",
        "build": "parcel build --no-source-maps"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@types/react": "^18.2.18",
        "@types/react-dom": "^18.2.7",
        "parcel": "^2.9.3",
        "postcss": "^8.4.27",
        "process": "^0.11.10",
        "tailwindcss": "^3.3.3",
        "typescript": "^5.1.6"
    },
    "dependencies": {
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
    }
}

Read more about source

The source field within any target declared in package.json can specify one or more entry files that are specific to that target. For example, you could build your frontend and backend simultaneously, or your desktop and mobile apps. See below for details about configuring targets.

{
  "targets": {
    "frontend": {
      "source": "app/index.html"
    },
    "backend": {
      "source": "api/index.js"
    }
  }
}

Now we can just npm run start to develop and npm run build to build.

Parcel supports Hot reloading. As you make changes to your code, Parcel automatically rebuilds the changed files and updates your app in the browser

CORS policy

If you access dist/index.html and get CORS policy error

access to script at 'file:///index.e59da450.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.

It is because we use ES6 module for script. ES6 modules are subject to same-origin policy, we can’t run it locally without served from a server.

Take a look at Web Extension

Read more