Creating a Switch: Part II

15m

Intermediate

Welcome to part two of Designing a Switch in Framer, a short tutorial series where we’ll be creating and using a code component in Framer X.

In Part I, we:

  • Set up our Framer X workspace
  • Created a code component for our Switch
  • Defined our component’s state logic
  • Presented our component’s state using animations

In Part II, we’ll:

  • Add props to our Switch, so that we can set its state from outside of the component
  • Add property controls to our Switch, so that we can set our component’s props through Framer X’s user interface

If you’re new to Framer X (or to React), then this tutorial is a great introduction to working with code. I’d recommend completing Part I before working through this tutorial.

Getting Started

We’ll be making this project with Framer X. If you don’t have it already, download a free 14-day trial.

Defining our component’s props

While our switch is already working, our component is so far a closed system: it has no way of getting information from other components. While our switch is switching, it can’t be switched! In order to make our switch more social, we’ll need to use props. As we discussed in the last tutorial:

Props are data that comes from the outside world. If a component is a function — and it technically is — then props are an argument passed to that function. If a component is a pizza, then props are the pizza order — toppings, crust, etc. Learn more about components and props.

Adding our first prop: isOn

For our component’s first prop, we’ll add a prop that will let us set the Switch’s initial state to on or off. Our component’s state is stored as isOn and we’ll use the same name for our prop.

First, let’s add isOn to our component’s default props.

  1. Below our component, add a set of defaultProps as shown below.
  2. Create a default prop for isOn and set it to false.
import * as React from "react"
import { Frame } from "framer"
export function Switch(props) {
...
}
Switch.defaultProps = {
isOn: false
}
COPY

Next, let’s tweak our component so that it uses this prop to set the component’s initial state.

  1. Find the setState hook at the top of our component.
  2. Replace the value of isOn with props.isOn.
const [state, setState] = React.useState({
isOn: props.isOn
})
COPY

We can test that it’s working by changing the value of defaultProps.isOn between true and false. When we reload our Preview, we should see that the component starts on or off depending on this value.

Adding a property control

Next, let’s add a property control. In Framer X, property controls give us the means to set our props from the canvas. In our case, we’ll create a little switch for our switch, so that we can set props.isOn from the canvas.

  1. At the very top of our project, add ControlType and addPropertyControls to the list of imports from the framer library.
  2. Below our component’s defaultProps, use addPropertyControls as shown below to define our isOn property control.

The addPropertyControls is a function that takes two arguments: a component and an object of property controls. In this object, we define each control under the name of the prop that we want to control. We won’t go deeper into the details here, so be sure to bookmark our guide to property controls.

import * as React from "react"
import { Frame, addPropertyControls, ControlType } from "framer"
export function Switch(props) {
// ...
}
Switch.defaultProps = {
isOn: false
}
addPropertyControls(Switch, {
isOn: {
type: ControlType.Boolean,
title: "On",
defaultValue: false
}
})
COPY

Now let’s jump back to our canvas to see the results.

On the right properties panel, we can see a new control named On.

Unfortunately, flipping the value on or off won’t have any effect on our switch on the canvas. However, if we create some copies of our component (by holding the Option key and dragging the instance), then we’ll see that the copies are respecting the prop. What’s going on?

The problem is that while our props.isOn sets the component’s initial state, it doesn’t control the state beyond that. The real logic we want is something like:

  • When the switch first mounts, use props.isOn to set the initial state.isOn.
  • If props.isOn ever changes, and if the change is different from our current state.isOn, then set state.isOn to the new props.isOn.

Adding an effect

To solve this problem, we’re going to need to use a second React hook, useEffect.

  1. In our component, find a space just after our useState hook.
  2. Add the useEffect hook from the snippet below.
const [state, setState] = React.useState({
isOn: props.isOn
})
React.useEffect(() => {}, [])
COPY

A useEffect hook is a function that takes two arguments: a function and an array of variables. We call the function an effect and we call the array of variables the effect’s dependencies. It works like this:

  • React will call the effect immediately after the component’s first render.
  • Every time the component renders, React will compare the values of its dependencies against their values from the previous render.
  • If it finds any changes, React will call the effect again.

In our case, we want the effect to run when props.isOn changes, so let’s add that to the effect.

React.useEffect(() => {}, [props.isOn])
COPY

When the effect runs, we want to compare the values of state.isOn and props.isOn. If they’re different, then we want to update state.isOn to match props.isOn.

React.useEffect(() => {
if (state.isOn !== props.isOn) {
setState({
isOn: props.isOn
})
}
}, [props.isOn])
COPY

Let’s return to the canvas and see where we’re at.

Ok, that’s better!

Our finished component

Let’s take another look at our component.

import * as React from "react"
import { Frame, addPropertyControls, ControlType } from "framer"
export function Switch(props) {
const [state, setState] = React.useState({
isOn: props.isOn
})
React.useEffect(() => {
if (state.isOn !== props.isOn) {
setState({
isOn: props.isOn
})
}
}, [props.isOn])
const flipSwitch = () => {
props.onValueChange(!state.isOn)
setState({
isOn: !state.isOn
})
}
return (
<Frame
height={50}
width={80}
center
borderRadius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" }
}}
initial={state.isOn ? "on" : "off"}
animate={state.isOn ? "on" : "off"}
transition={{
type: "tween",
duration: 0.2
}}
>
<Frame
size={46}
top={2}
left={2}
borderRadius={"100%"}
background="#FFFFFF"
variants={{
off: { x: 0 },
on: { x: 30 }
}}
transition={{
type: "tween",
duration: 0.2
}}
/>
</Frame>
)
}
Switch.defaultProps = {
isOn: false,
onValueChange: () => null
}
addPropertyControls(Switch, {
isOn: {
type: ControlType.Boolean,
title: "On",
defaultValue: false
}
})
COPY

Just like last time, we’ll walk through each part that we’ve added. Anything not covered below was code from Part I, and you can read about it there.

Line 2: We added addPropertyControls and ControlType to the list of imports from the “framer” library.

Line 6: We changed our setState function to use props.isOn to set the component’s initial state.

Lines 9 – 15: We added a useEffect hook that runs when our component receives a different value for props.isOn and, if necessary, updates state.isOn to match props.isOn.

Lines 60 – 62: We added a set of defaultProps, ensuring that our component will always have a value for props.isOn.

Lines 64 – 70: Finally, we added a set of property controls to our component. At the moment, our property controls only include one control for the isOn prop.


Download the finished project ›

Next step: Interactions

In this tutorial, we learned about how to get information into our component from the outside world through props. We also added our first property controls, so that we could set our props from the canvas. We also learned to use a useEffect hook to control the logical relationship of props and state.

However, while our switch can now receive information from the outside world, it still has no way of sharing information with other components. In other words, our switch can be switched, but it can’t switch anything else!

In the final part of this three-part series, we’ll give our component a new prop named onValueChange and we’ll learn to use it with overrides to control other parts of our prototype.

Feel free to reach out on Twitter if you run into any difficulties with this tutorial, or join one of Framer’s incredibly supportive community-run groups on Facebook or Slack. Or continue your learning streak using some of the following resources: