cyberangles blog

How to Import React Packages (Including useState) Using CDN Links Without NPM

React has revolutionized front-end development with its component-based architecture and powerful features like hooks (e.g., useState). Traditionally, React projects rely on package managers like npm or yarn to install dependencies, transpile code (e.g., JSX), and bundle assets. However, there are scenarios where setting up a full npm environment isn’t feasible—for example, quick prototyping, small static websites, or environments with limited access to Node.js.

In such cases, using CDN (Content Delivery Network) links to import React and its tools is a lightweight alternative. CDNs host pre-built versions of libraries, allowing you to include them directly in your HTML file without npm. This guide will walk you through importing React, ReactDOM, and even using hooks like useState via CDN, with step-by-step examples and troubleshooting tips.

2025-12

Table of Contents#

  1. Understanding CDNs for React
  2. Prerequisites
  3. Step-by-Step Guide to Importing React via CDN
  4. Common Issues and Solutions
  5. Advantages and Limitations of CDN-Based React
  6. Conclusion
  7. References

Understanding CDNs for React#

A CDN is a network of servers that deliver files (like JavaScript libraries) to users based on their geographic location, ensuring fast load times. For React, CDNs host pre-compiled versions of the library, eliminating the need for local installation.

Key points about React and CDNs:

  • React and ReactDOM: React is split into two core packages: react (the core library) and react-dom (for rendering components to the DOM). Both are available via CDN.
  • UMD vs. ES Modules: CDNs like unpkg or cdnjs offer two formats:
    • UMD (Universal Module Definition): Exposes React as a global variable (e.g., window.React), ideal for beginners.
    • ES Modules: Uses import/export syntax (requires type="module" in script tags).
  • Development vs. Production Builds: CDNs provide separate builds:
    • development.js: Unminified with debug warnings (for local testing).
    • production.min.js: Minified, optimized, and smaller in size (for live sites).

Prerequisites#

Before starting, ensure you have:

  • A basic understanding of HTML, JavaScript, and React fundamentals (e.g., components, hooks).
  • A code editor (e.g., VS Code).
  • A modern browser (Chrome, Firefox, Edge, etc.).
  • No Node.js or npm required!

Step-by-Step Guide to Importing React via CDN#

Step 1: Set Up the Basic HTML File#

Start with a minimal HTML template. This will serve as the foundation for your React app:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React CDN Example</title>
</head>
<body>
    <!-- React app will render here -->
</body>
</html>

Step 2: Import React and ReactDOM via CDN#

Next, include React and ReactDOM using CDN links. We’ll use unpkg (a popular CDN for npm packages) for simplicity. Add these script tags inside the <head> section:

<!-- React (core library) -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
 
<!-- ReactDOM (for rendering to the DOM) -->
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  • react@18: Specifies React version 18 (check React’s docs for the latest version).
  • umd/react.development.js: The UMD (Universal Module Definition) build, which exposes React as a global variable (accessible via window.React).

Step 3: Add Babel for JSX Transpilation#

Browsers don’t natively understand JSX (React’s HTML-like syntax). To convert JSX into valid JavaScript, we need to include Babel via CDN. Babel is a transpiler that converts modern JavaScript (and JSX) into browser-compatible code.

Add the Babel CDN link below the React scripts in the <head>:

<!-- Babel (transpiles JSX to JavaScript) -->
<script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>

Step 4: Create a Root Element in HTML#

React needs a "root" DOM element to render your app. Add a <div> with an id (e.g., root) inside the <body>:

<body>
    <!-- Root element for React -->
    <div id="root"></div>
</body>

Step 5: Define a React Component with useState#

Now, let’s write a React component that uses the useState hook. Create a <script> tag with type="text/babel" (this tells Babel to transpile the code inside).

Inside this script, define a component (e.g., Counter) that uses React.useState (since useState is a method of the global React object when using UMD):

<script type="text/babel">
  // Define a component using useState
  function Counter() {
    // Initialize state: count = 0, setCount = updater function
    const [count, setCount] = React.useState(0);
 
    return (
      <div style={{ textAlign: "center", marginTop: "50px" }}>
        <h1>Counter: {count}</h1>
        <button onClick={() => setCount(count + 1)}>Increment</button>
        <button onClick={() => setCount(count - 1)}>Decrement</button>
      </div>
    );
  }
</script>
  • React.useState(0): Initializes a state variable count with a default value of 0 and returns an updater function setCount.
  • JSX is used here (e.g., <div>, <button>), which Babel will convert to React.createElement calls.

Step 6: Render the Component to the DOM#

Finally, render the Counter component into the root element using ReactDOM.createRoot (React 18+ syntax). Add this code at the end of the text/babel script:

<script type="text/babel">
  // ... (Counter component from Step 5)
 
  // Render the Counter component to the root element
  const root = ReactDOM.createRoot(document.getElementById("root"));
  root.render(<Counter />);
</script>
  • ReactDOM.createRoot: Creates a root for rendering React components (replaces the older ReactDOM.render method in React 18).

Full Working Example#

Here’s the complete HTML file with all steps combined:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React CDN Counter Example</title>
    
    <!-- React and ReactDOM CDN -->
    <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    
    <!-- Babel CDN for JSX transpilation -->
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
</head>
<body>
    <!-- Root element for React -->
    <div id="root"></div>
 
    <!-- React component with useState -->
    <script type="text/babel">
        function Counter() {
            const [count, setCount] = React.useState(0);
 
            return (
                <div style={{ textAlign: "center", marginTop: "50px" }}>
                    <h1>Counter: {count}</h1>
                    <button onClick={() => setCount(count + 1)}>Increment</button>
                    <button onClick={() => setCount(count - 1)}>Decrement</button>
                </div>
            );
        }
 
        // Render the app
        const root = ReactDOM.createRoot(document.getElementById("root"));
        root.render(<Counter />);
    </script>
</body>
</html>

Save this as index.html and open it in a browser. You’ll see a working counter with increment/decrement buttons!

Common Issues and Solutions#

1. "JSX is not defined" or Syntax Errors#

  • Cause: Babel isn’t transpiling JSX.
  • Fix: Ensure the script tag containing JSX has type="text/babel".

2. "useState is not a function"#

  • Cause: React isn’t loaded, or you’re using useState without React..
  • Fix: Verify the React CDN links are correct. Use React.useState (not just useState).

3. "ReactDOM.createRoot is not a function"#

  • Cause: Outdated React version (e.g., React 17 or earlier uses ReactDOM.render instead).
  • Fix: Use the latest React 18+ CDN links (e.g., react@18).

4. Slow Performance in Production#

  • Cause: Development builds (development.js) are unminified and include debug code.
  • Fix: For production, use minified builds:
    <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

Advantages and Limitations of CDN-Based React#

Advantages#

  • Zero Setup: No npm, Node.js, or build tools required.
  • Quick Prototyping: Ideal for CodePen, JSFiddle, or small demos.
  • Lightweight: No node_modules folder cluttering your project.

Limitations#

  • No Build Optimization: No tree-shaking, code splitting, or minification (unless using production CDN builds).
  • Global Variables: Risk of conflicts with other libraries using global React or ReactDOM.
  • No Hot Reloading: Changes require manual browser refreshes.
  • Not for Large Apps: Scaling becomes difficult without a bundler (e.g., Webpack).

Conclusion#

Using CDN links to import React and useState is a powerful way to build React apps without npm. It’s perfect for quick demos, learning, or small static projects. By including React, ReactDOM, and Babel via CDN, you can write JSX and use hooks directly in HTML.

For larger apps, however, npm-based setups with tools like Vite or Create React App are still better due to build optimization and scalability. Use CDN for simplicity, and graduate to npm when your project grows!

References#