React Hooks (and What They Mean for Designers)What React Hooks Mean for Designers

Learn how Hooks make React significantly easier to write and read, along with some specific examples on how to use them for rapid prototyping.


Zach Johnston

May 9, 2019

Zach spent many years working on design systems for the Dropbox Paper team and is currently teaching himself React & C4D.

React recently introduced Hooks and it radically simplified how I create components. This post walks through the basics of Hooks and how to use them for quick prototyping.

Function components

When I started learning React, my first exercise was to create a simple toggle component. It accepted a single prop and created a toggle switch in either the on or off position.

function Toggle(props) {
return (
<div className={"toggle " + (props.enabled ? "on" : "off")}>
<div className="toggle_inner"></div>

With a little extra CSS, I now had this cool little toggle component that I could drop into my prototypes. Hooray!

Now to make the toggle change position on click. Shouldn’t be too hard, right? I soon learned that my toggle was a function component and, if I wanted it to be interactive, I would need to use a class component.

Function components are sometimes called stateless or dumb components because they can’t really do anything unless their parent tells them to. Unless the parent of the toggle manually changes the toggle’s enabled prop, the toggle is never going to change positions. We could make the parent listen for a click and manually change the toggle’s position, but then the toggle isn’t really a self-contained component.

Class components, on the other hand, can manage their own state. We can store the toggle’s position as a state variable and on click update the position. Since the class component can update its own state without communicating with the parent, we can keep the component fully self-contained.

This calls for a class component

Once I understood the benefits of state, I rewrote my functional component as a class component. Now, instead of the toggle’s position being controlled by a static props.enabled value, it was managed by this.state.enabled.

I had successfully replaced the enabled prop with a state, I still wanted to write something like <Toggle enabled={true} /> to control the default position of the toggle. To accomplish this little detail, I spent hours learning about the constructor lifecycle method, the still confusing super(props), and how a prop can set the default state value.

class Toggle extends React.Component {
constructor(props) {
this.state = { enabled: props.enabled }
handleClick = () => {
this.setState({ enabled: !this.state.enabled })
render() {
return (
className={"toggle " + (this.state.enabled ? "on" : "off")}
<div className="toggle_inner">s</div>

Finally, after 25 lines of code and a lot of Stack Overflow, I had a toggle component that changed positions when clicked. For someone without a technical background, this was a bit more complex than I had hoped.

I loved the power of state and lifecycle but when I’m prototyping, most of my code gets thrown away. I needed something a bit more lightweight.

React hooks to the rescue

Hooks take the best parts of class components and bring them to the lightweight function components. You can think about Hooks as special functions that have access to state and lifecycle features and you can recognize them by their use prefix.

Making a new Hook is a bit advanced but thankfully React comes with two handy Hooks built in: useState and useEffect.

The State Hook

Let’s rewrite our toggle as a function component with the useState Hook.

function Toggle({defaultEnabled}) {
const [enabled, setEnabled] = useState(defaultEnabled);
return (
className={"toggle " + (enabled ? "on" : "off")}
<div className="toggle_inner"></div>

What took 6 lines with class components can now be accomplished with just 1 line using Hooks. That one line of code is a function that takes in a default state argument and returns two things: a way to read the state and a way to change it.

Don’t worry if you can’t remember the exact syntax. You can copy this line and simply replace the 3 values with whatever makes sense for your component.

const [enabled, setEnabled] = useState(false)
const [language, setLanguage] = useState("english")
const [upvotes, setUpvotes] = useState(0)
setUpvotes(upvotes + 1)

Get familiar with this pattern because you’ll likely be using it in almost any prototype that has interactive elements.

One last thing: in our class component we used the constructor lifecycle method to set the default state to mirror a prop. With Hooks, we can pass a prop like defaultEnabled directly in as the default value like this <Toggle defaultEnabled={false} />.

const [enabled, setEnabled] = useState(false) // off by default
const [enabled, setEnabled] = useState(true) // on by default
const [enabled, setEnabled] = useState(defaultEnabled) // dynamic default

And just like that you’ve learned to create and set state with the useState Hook. If you should feel confident enough to create a simple component, like a counter or a popover.

Next, we’re going to learn about a type of Hook that lets us run code after the component renders.

The Effect Hook

If we want to add keyboard shortcuts to your prototype, transition an object when the user scrolls, or run any other code after a component renders, we’ll need to learn about the useEffect Hook. If you’re coming from class components, it might help to know that the useEffect Hook replaces 3 lifecycle methods: componentDidMount, componentDidUpdate, and componentWillUnmount.

useEffect(() => {
// Run code after the component renders
}, [])

Any code placed inside the useEffect Hook will run after the component is first rendered which is perfect for adding keydown or scroll event listeners.

Let’s create a modal component that closes when a user hits the Escape key:

function Modal() {
const [open, setOpen] = useState(true)
function downHandler({ key }) {
if (key === "Escape") {
useEffect(() => {
window.addEventListener("keydown", downHandler)
return () => {
window.removeEventListener("keydown", downHandler)
} // This return function cleans up the event listener
}, []) // The empty brackets means this only runs once
return (
<div className={"modal " + (open ? "isOpen" : "isClosed")}>
Hello World!

We are adding a keydown event listener, wrapped inside the useEffect function, which will call the downHandler function any time the user presses a key. If the keypress matches “Escape”, we close the modal by updating the open state to false.

This is a fairly basic use case, but if you want to learn more about the Effect Hook (like why there is an empty array [] ), I recommend reading A Complete Guide to useEffects by Dan Abramov.

Tips for making the switch to React Hooks

If you’re like me and making the switch from class components to function components, there are a few syntax changes and conceptual speed bumps that may come as a surprise:

No more 'this'

A class component uses this to reference itself (e.g. this.handleClick to call a method attached to the class). Function components, however, don’t have this so you can leave that concept in the past.

Don’t forget the (props)

If you want your avatar component to have an image prop, you will need to explicitly tell the function to accept a props argument. You can write Avatar(props) to reference props.image, or better yet use Avatar({image}) to make referencing the image prop as simple as writing image.

Functions as consts

While we no longer need to write this, we do need to be a bit more verbose when writing local functions by putting const before the function name. For example, if you want a handleClick function, you’ll need to write it as const handleClick.

Bye, bye render()

Function components do not have a render method so you can put your JSX inside a return statement at the top level of your function. Much cleaner!

Real world examples

Overall, function components with Hooks are much easier to understand than class components, but that doesn’t mean it won’t take some practice before you’re comfortable using them in your next project. Here are a few more examples to help these concepts sink in:

Checking an email address

Maybe your prototype requires the user to enter a work email. This input reminds the user to enter a work email if they use a address. Simple, but effective!

function EmailInput({defaultValue}) {
const [value, setValue] = useState(defaultValue);
const handleInputChange = event => {
const Email =
if (Email.includes("")) alert("enter a work email")
return <input value={value} onChange={handleInputChange} />

Unsplash API

This component loads in a random image from the Unsplash API. We run updateImage() after the first render using the Effect Hook, and any time the <img /> tag is clicked.

function UnsplashImage({size, search}) {
const [src, setSrc] = useState(null)
const updateImage = () => {
const baseUrl = ""
const route = search === "" ? "/random" : "/featured"
const url = `${baseUrl}${route}/${size}x${size}?${search}`
fetch(url).then(response => setSrc(response.url))
useEffect(() => {
}, [size, search])
return (
<img src={src} onClick={updateImage} />
Unsplash api

Even more React Hook examples

If you’re hungry for more Hooks examples, check out by Gabe Ragland. You’ll find dozens of high-quality Hook examples and each one of them can be copied into your next prototype as is. One of my personal favorites is the useDarkMode Hook.

Use Hooks in the new Framer Playground

With Framer X22, you can now prototype with React Hooks in the built-in code editor. If you’re new to React this is a great way to see Hooks in action without dealing with the added complexity of creating a full React app.

Feel free to reach out on Twitter if you have any questions on Hooks, or join one of Framer’s incredibly supportive community-run groups on Facebook or Slack. For more content on React that caters specifically to designers, check out some of the following resources:

Like this article? Spread the word.

Sign up for our newsletter

Join our newsletter and get resources, curated content, and design inspiration delivered straight to your inbox.