Can you build a React app without using the NPM (node package manager) or Yarn (Yet Another Resource Negotiator)? Yes, you absolutely can. React is a JavaScript library you can manually import into any web page. Let’s look at building a simple React component without npm.
You can jump straight to the source code here
This only intends to show what React is doing without JSX and Babel. It is also an excellent way to add React to a page on a site where you only have a limited need for React components.
You should also note that the first few steps of this process are mentioned in greater detail in the React documentation found here.
The first step is to create the HTML page for your site. Let’s call this index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Without NPM</title>
</head>
<body>
</body>
</html>
For VS Code users, if you hit SHIFT+1, you can quickly select the HTML template to create the above code (without the title).
Importing The React Library Into Your HTML
To use React without npm, we need access to the React API. This is done by adding the React library as a source in our HTML file.
Add the following script tag at the end of your HTML body tag above, and you’ll have access to the React API.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Without NPM</title>
</head>
<body>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</body>
</html>
The two scripts included are the development versions of the React library. These are not intended for production.
Creating A React Component Without NPM
With the boilerplate HTML complete, the next thing we need to do is create our component. Let’s create a new JavaScript file where we will code our component. We can call this file Feedback.js
If you want to see a complete React component written from scratch, you can see that here.
The Feedback component will be a button at the bottom right of the screen and pop out a text area for comments. The idea is to show how to add a useful React component without NPM.
The first step is to create a function named Feedback that returns an element. You can return an element using the createElement function in the React library.
The first createElement parameter is type. This type can be a regular HTML element or a React component. As we create a button component, we’ll use ‘button’ for the type parameter.
The second parameter is props. We’ll alert the user when the button is clicked for our initial implementation. This means we can pass an onClick function as a prop to our component.
The third parameter is an option children parameter. Let’s leave this blank initially.
The Initial Component
function Feedback() {
return React.createElement(
'button',
{
onClick: () => alert('Hello Bernie'),
},
'Click me',
);
}
The HTML Entry Point
Now that we have a basic component defined, we need somewhere to show it. To do this, you need to add an element in the HTML that will be the location of where the component will be shown.
Let’s create a div element that will act as the entry point to the React tree of components. We will give the div an id attribute so that we can inject our React component.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Without NPM</title>
</head>
<body>
<div id="react-entry-point"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</body>
</html>
To use the new div as the entry point, we need to update our JavaScript. We need first to get a reference to the div and then tell React to create a root node at this div. Finally, we need to render the component. This is done with the following JavaScript:
const rootNode = document.getElementById('react-entry-point');
const root = ReactDOM.createRoot(rootNode);
root.render(React.createElement(Feedback));
These lines of JavaScript appear at the bottom of our Feedback.js file:
function Feedback() {
return React.createElement(
'button',
{
onClick: () => alert('Hello Bernie'),
},
'Click me',
);
}
const rootNode = document.getElementById('react-entry-point');
const root = ReactDOM.createRoot(rootNode);
root.render(React.createElement(Feedback));
Finally, we need our HTML file to know about our JavaScript file. We do this with another script tag:
<script src="Feedback.js"></script>
We can put this with our other script tags:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React Without NPM</title>
</head>
<body>
<div id="react-entry-point"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="Feedback.js"></script>
</body>
</html>
The Output So Far
If we open our index.html file in a browser, you will see the following:
A More Complicated React Component Without NPM
Using React without NPM doesn’t mean the components must be entirely simple. For our feedback component, we currently use a button element. Let’s change this to a div element containing our button and the text area for users to leave feedback comments.
The following code is a complete component built off the createElement function:
function Feedback() {
const [buttonText, setButtonText] = React.useState("Give Feedback");
const [buttonColor, setButtonColor] = React.useState("darkGreen");
const [textAreaText, setTextAreaText] = React.useState("");
const [showTextArea, setShowTextArea] = React.useState(false);
return React.createElement(
"div",
{
style: {
display: "flex",
flexDirection: "column",
gap: "20px",
width: "450px",
position: "fixed",
right: "15px",
bottom: "15px",
zIndex: "1000",
alignItems: "flex-end",
},
},
React.createElement("textarea", {
placeholder: "Type your feedback comments here...",
style: {
display: showTextArea ? "block" : "none",
height: "150px",
padding: "12px 20px",
boxSizing: "border-box",
border: "2px solid #ccc",
borderRadius: "12px",
backgroundColor: "#f8f8f8",
fontSize: "16px",
resize: "none",
width: "100%",
},
value: textAreaText,
onChange: (e) => setTextAreaText(e.target.value),
}),
React.createElement(
"button",
{
style: {
border: "3px solid " + buttonColor,
borderRadius: "32px",
backgroundColor: buttonColor,
fontSize: "20px",
color: "white",
width: "150px",
padding: "15px",
},
onClick: () => {
if (buttonColor === "darkGreen") {
setButtonText("Submit Feedback");
setButtonColor("orange");
setShowTextArea(true);
} else {
// Make ajax post request to send the feedback to some endpoint
setShowTextArea(false);
setTextAreaText("");
setButtonText("Submitted");
setButtonColor("blue");
}
},
},
buttonText
)
);
}
const rootNode = document.getElementById("react-entry-point");
const root = ReactDOM.createRoot(rootNode);
root.render(React.createElement(Feedback));
Let’s recap what we want the Feedback component to do. Firstly, when the user clicks the button, the text area should become visible for users to enter feedback. Secondly, the user enters their feedback. Thirdly, they can click the button to submit it. Pretty straightforward.
Using State With React.createElement
The start of the Feedback function now uses React.useState to add state to the component. These state values relate to the colour and text of the button, the text of the text area and whether the text area is currently visible. I’ve highlighted all uses of state throughout the component.
Using The Children Parameter Of React.createElement
The Feedback component is a div element with textarea and button as its children. I have used React.createElement to create the children. The state of the Feedback component drives these children. You can see this in lines 36-37 and 51-62.
Lines 36-37 manage the text in the textarea element. Lines 51-62 manage the visual state and what happens when clicking the button.
CSS Of the Elements
The CSS of the elements are pretty basic. I have fixed the position of the Feedback component to the bottom right of the screen. The remaining styling set is there to make the elements look nice.
To highlight this fixed position, I have added some auto-generated lorem ipsum text to make the page scroll.
The Final Product Of A React Component Without NPM
This is what the button looks like on a page with some text.
Can I Use JSX Without NPM?
Yes, you can use JSX without NPM. Let’s update the above component to use JSX.
Firstly, we must add the babel source to our script section (before our component).
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
Now that Babel is imported, we can update our Feedback component to JSX.
There is one more step, and that is to update the type of the import script to text/babel for our Feedback component:
<script src="Feedback.jsx" type="text/babel"></script>
The resulting component looks like this:
function Feedback() {
const [buttonText, setButtonText] = React.useState("Give Feedback");
const [buttonColor, setButtonColor] = React.useState("darkGreen");
const [textAreaText, setTextAreaText] = React.useState("");
const [showTextArea, setShowTextArea] = React.useState(false);
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "20px",
width: "450px",
position: "fixed",
right: "15px",
bottom: "15px",
zIndex: "1000",
alignItems: "flex-end",
}}
>
<textarea
placeholder="Type your feedback comments here..."
style={{
display: showTextArea ? "block" : "none",
height: "150px",
padding: "12px 20px",
boxSizing: "border-box",
border: "2px solid #ccc",
borderRadius: "12px",
backgroundColor: "#f8f8f8",
fontSize: "16px",
resize: "none",
width: "100%",
}}
value={textAreaText}
onChange={(e) => setTextAreaText(e.target.value)}
></textarea>
<button
style={{
border: "3px solid " + buttonColor,
borderRadius: "32px",
backgroundColor: buttonColor,
fontSize: "20px",
color: "white",
width: "150px",
padding: "15px",
}}
onClick={() => {
if (buttonColor === "darkGreen") {
setButtonText("Submit Feedback");
setButtonColor("orange");
setShowTextArea(true);
} else {
// Make ajax post request to send the feedback to some endpoint
setShowTextArea(false);
setTextAreaText("");
setButtonText("Submitted");
setButtonColor("blue");
}
}}
>
{buttonText}
</button>
</div>
);
}
Hopefully, this will help anyone trying to add a non-trivial React component to a non-React website. Thanks for reading!