Skip to content

Intro to React for HubSpot Developers

A web developer's career requires constant learning. In my experience, I've learned 95% of my development skills outside the classroom as the web is ever-evolving. I started out doing heavy HTML and CSS type work with light JavaScript, then PHP then HubL, over time client side functionality became more and more important. For sites needing interactivity jQuery was an early solution, then JavaScript itself eliminated the majority of reasons I used jQuery. Projects became more complex, we're not just toggling menus and accordion menus, we're building pricing calculators, shopping carts, vehicle customizers. Suddenly I was building web apps that searched and filtered multiple databases, worked offline, had to load super fast, be accessible, and work in a variety of browsers. JavaScript frameworks like React and Vue helped to simplify the build out of complex user interfaces.

I didn't start by learning React, or Vue, I learned everything incrementally over years and the things I learned early on are a foundation for learning new things. My goal is to give you an introduction to ReactJS by building on ideas you may already be familiar with. This post isn't a replacement for a course on React or the React Quick Start documentation, but it is intended to help you to understand those resources even better and make you ready for them. This post will also be useful if you're coming back to React after a period of not touching it, and just wanting a refresher.

We'll cover:

What is React JS?

React JS is an open-source JavaScript library for building user interfaces, developed by Meta. React allows developers to create reusable UI components in a declarative way - which makes the code easier to read and understand. As web interfaces become more and more complicated they start to have a lot of different pieces of state you are trying to track and handle in your UI.

The terms components, declarative, and state may be hazy or unfamiliar to you, that's okay I'll explain each.

What does it mean to be declarative?

You've probably written JavaScript before that felt like you were telling the computer, "when the button is clicked, change this HTML". You were outright telling the computer what you wanted it to do. This is called imperative programming.

// You have 1 button on a page, and an HTML output element that you're displaying text in once the button is clicked. let button = document.getElementById("myButton"); let output = document.getElementById("output"); button.addEventListener("click",(e) => { output.textContent="The button was clicked!" });

View on CodePen.

The more complexity and interactions you need to build, this code gets cumbersome, and messy, because you have to consciously think about every combination of interactions a user can make and how they affect each other.

// you have two buttons and an HTML output element that you are displaying the current count in - determined by increment and decrement button clicks. let incrementButton = document.getElementById("increment"); let decrementButton = document.getElementById("decrement"); let count=0; let output = document.getElementById("output"); incrementButton.addEventListener("click",(e) => { count++; output.textContent=`The count is at: ${count}`; }); decrementButton.addEventListener("click",(e) => { count--; output.textContent=`The count is at: ${count}`; });

View on CodePen.

This is a really simple example, but already we're in a situation where we have to create 2 variables for 2 buttons, add click handlers, then increment/decrement the count based on which gets clicked and update the output text. It's getting verbose, and we have duplicate code for setting the output text. We could refactor that into a single function or make "The count is at:" a string variable so we only have 1 place to update it. Now in small projects this is completely fine, but as projects grow in complexity it gets harder and harder to maintain and reason about your code.

Programming declaratively means instead of telling the computer what to do, you write code that describes the desired output or result, and let the computer figure out how to produce that result. Stating what the HTML should look like in each state of your UI based on the variables you have. This way of programming becomes significantly more useful the more UI elements and different pieces of state you need your UI elements to handle.

Here's that same example of an increment and decrement number counter done in React.

import React, {useState} from '' import ReactDOM from '' function App() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(count + 1)}> Increment </button> <output>The count is at {count}</output> <button onClick={() => setCount(count - 1)}> Decrement </button> </> ); } ReactDOM.render(<App />, document.getElementById("root"))

View on CodePen.

Without any prior React experience you can probably already intuit a bit of what this code does even if you don't understand how to write it. The const is our count variable, there's code that looks like our HTML, the buttons even have onClick events that just increase or decrease the count variable.

In React you write HTML-like code called JSX (JSX literally means JavaScript XML, but you can think of JSX as "fancy HTML in JavaScript"). In JSX you define the structure of your UIs through components, then react takes care of managing state and updates your UI automatically as the state changes. You don't write or need to think about DOM (Document Object Model) manipulation, element selectors, and do I use .innerHTML here or .textContent?  By taking that responsibility off your shoulders that allows you to write more concise, reusable and maintainable code.

There are some key differences between HTML and JSX that you should know. HTML doesn't require every element to be closed <img src="">, JSX does, <img src=""/>.  

JSX is still JavaScript - which means JavaScript and variables can be intermingled with your HTML-like markup. If you've been building with HubL or any other server-side templating language this intermingling of HTML, your variables, and logic, should feel familiar. Same idea - but on the front-end.

What's a component?

A React component is a reusable piece of code that describes the structure, and behavior of a part of a user interface in a web app or website. Components are abstractions that make writing and describing complicated structures in your code easier. For me personally, what helps is relating them to what I already know.

React components are similar to HTML elements, HubSpot modules in CMS, and native web components - in an increasing fashion in that order.

React components are similar to an HTML element, to use them you write HTML- like code, with a beginning and ending tag, or make them self-closing. They have attributes - in React components we call these props (properties).

import React, {useState} from '' import ReactDOM from '' function MyButton({btnType,children}) { return ( <> <button className={`btn btn-${btnType}`}>{children}</button> </> ); } ReactDOM.render(<MyButton btnType="primary">My Button's text</MyButton>, document.getElementById("root"))<button class="btn btn--primary">My button's text</button>{% module "button" path="@hubspot/button" button_text="My button's text" %} <my-button>My Button's text</my-button>

Components are reusable, so you can have multiple separate instances of the component on the page. Each instance can be different, have different props making them look or function differently. This is also similar to passing field values in HubSpot modules.

function MyButton({ btnType, children }) { return ( <> <button className={`btn btn-${btnType}`}>{children}</button> </> ); } function MyApp() { return ( <> <MyButton btnType="primary">Save</MyButton> <MyButton btnType="warning">Delete</MyButton> </> ); } ReactDOM.render(<MyApp></MyApp>, document.getElementById("root"));<button class="btn btn--primary">Save</button> <button class="btn btn--warning">Delete</button>{% module "button" path="@hubspot/button" button_text="Save" %} {% module "button2" path="@hubspot/button" button_text="Delete" %} <my-button data-type="primary">Save</my-button> <my-button data-type="warning">delete</my-button>

They usually have a base visual styling so a button for example looks like a button anywhere you use it without you having to re-style each instance individually.

React components are effectively custom HTML elements that you create. To be able to use a component we have to define it - tell React what it looks like and how it behaves. This isn't some special wizardry, you're actually using the same skills you've built up over your career. You are creating components using HTML, CSS, and JavaScript. This is like HubSpot's custom modules, what you put in your module.html is your definition of what should be rendered every time you use the module's tag or a user adds the module to a page. If you're familiar with native web components, this is just like how you define the shadow DOM, each React component is just made up of HTML, CSS, and Javascript inside.

So a component called MyButton, literally may include the button HTML element, and have some special classes added to it.

import React, {useState} from '' import ReactDOM from '' function MyButton({btnType,children}) { return ( <> <button className={`btn btn-${btnType}`}>{children}</button> </> ); } ReactDOM.render(<MyButton btnType="primary">My Button's text</MyButton>, document.getElementById("root")) <button class="btn btn-{{ module.button_type }}"> {{ module.button_label }} </button> class myButton extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); } connectedCallback() { let btnType = this.getAttribute("data-type"); const btnClasses = `btn btn-${btnType}`; this.shadowRoot.innerHTML = ` <button class="${btnClasses}"><slot></slot></button> `; } } window.customElements.define("my-button", myButton);

Notice that the definition for this button component, in React is literally a JavaScript function that returns JSX.

Unlike HubSpot Modules, React components can be nested inside other React components. While you can create say a button component, you can also place that button inside of say an alert box so it can close the alert. That alert box could be inside of the content component of your UI, and that content component could be inside of another component for your full app.

import React, {useState} from '' import ReactDOM from '' function MyButton({btnType,children}) { return ( <> <button className={`btn btn-${btnType}`}>{children}</button> </> ); } function RowOfButtons({children}){ return( <div className="row-of-buttons"> {children} </div> ); } ReactDOM.render( <RowOfButtons> <MyButton btnType="primary">My Button's text</MyButton> <MyButton btnType="secondary">Other button text</MyButton> </RowOfButtons>, document.getElementById("root")) <rowof-buttons> <my-button data-type="primary">Save</my-button> <my-button data-type="warning">delete</my-button> </rowof-buttons>

What is state?

In React, state is an object that represents the current condition of an individual component at a given point in time and determines how it should render and behave. You can think of it as all of the variables needed within a component to make it function.

For example, if you have a slider component there is 1 obvious piece of state needed to display the slider correctly: Which slide is currently selected/shown.

Actions a user takes within the slider component such as clicking the next or previous buttons will change which slide is selected, but if the slider is only one part of a page/app then the other components in the page don't need to know what slide is currently selected.

function MyButton({btnType,children, handleClick}) { return ( <> <button className={`btn btn-${btnType}`} onClick = {() => handleClick}>{children}</button> </> ); } function RowOfButtons({children}){ return( <div className="row-of-buttons"> {children} </div> ); } function Slider({initialSlide}){ let [currentSlide, setCurrentSlide] = useState(1); function handleBtnClick(newSlide) { setCurrentSlide(newSlide); } return(<> <div data-current-slide={currentSlide}> <ul> <li><img alt="slide 1" src="img/1.png"/></li> <li><img alt="slide 2" src="img/2.png"/></li> <li><img alt="slide 3" src="img/3.png"/></li> </ul> </div> <RowOfButtons> <MyButton btnType="primary" handleClick={handleBtnClick(currentSlide-1)}>Previous Slide</MyButton> <MyButton btnType="secondary" handleClick={handleBtnClick(currentSlide+1)}>Next Slide</MyButton> </RowOfButtons> </>); } const container = document.getElementById('root'); const root = createRoot(container); // root.render(<Slider initialSlide={1} />);

The magic of React - how it gets it's name - is that changes to the state of a component, trigger the component to re-render. That means when the state object gets updated, React re-calculates what the component's HTML should be and efficiently does all of the DOM manipulation for you.

function CounterButton() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <button onClick={handleClick}> Button has been clicked {count} times </button> ); }

JavaScript doesn't have a built-in way to execute code based on when a variable is updated - so React has a function - referred to as a React hook. When you want to update your state variables you use your "set" function, which tells React - re-render this component.

If you want an action to happen based on a user's interaction, you use event handlers like onClick, but you change a component's state and write the component to handle for if the state is different, you don't touch the DOM with your code. If the first time you write a React component you have to rewrite a lot of it, don't beat yourself up. In my experience this declarative way of working takes a bit of getting used to. It requires a little bit of a mindset shift, and the only way to get it to really click, is to try and build something. Once it clicks for you it will open up a whole new way of development for you.

What are props?

We talked about how props are like attributes on HTML elements. They are a way to pass variables from parent components to child components.

function MyButton({btnType,children}) { return ( <> <button className={`btn btn-${btnType}`}>{children}</button> </> ); } ReactDOM.render(<MyButton btnType="primary">My Button's text</MyButton>, document.getElementById("root"))

Because variables in JavaScript can be arrays, objects, functions, and even react components (remember they're just functions that return JSX) - you can pass all of them as props to a child component.

Once passed in, your React component can use props just like any other variable used for rendering the component or calculating. They work effectively the same as arguments in a JavaScript function.

Where and how to keep learning

What I'm hoping you've gotten out of this so far is an understanding of React's core concepts so that when you see other React educational content your eyes won't glaze over when the concepts come up. Now that you have some foundational knowledge the best way to really make this sink in is to build something.

If you need some motivation, HubSpot is taking React seriously, whether you're building for CMS or building CRM extensions React is valuable to know.

Maybe this was a lot of information to digest, and you need a break today, but at least put it on your calendar for tomorrow, "20 minutes of React". In the description of that calendar event you're going to paste the link for a React tutorial. To start I encourage you to do the React Quick Start, and actually build along with it, you are ready, you can spend 20 minutes doing anything. If you don't complete it in one go, put another 20 minute event on your calendar for the next day. It's not about completing it fast, it's about making progress and putting what you learn into practice so it sticks.

Once you've done the React Quick Start here's my recommendation - come up with something to build that's your own idea. You will hit some roadblocks, but the React docs are there to help you. You can always also look back at the React Quick Start, your old code, and this article. If you are still stuck try searching the web or Stack Overflow. If you still come up empty, work on another part of your project and come back to it later.