Designing a Switch in Framer, Part I

Steve Ruiz
14 min readMay 7, 2019

--

A crash course in code components with the new Framer X API.

Introduction

Welcome to the first part of the 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’ll:

  • Set up our Framer X workspace.
  • Create a code component for our Switch
  • Define our component’s state logic
  • Present our component’s state using animations

If you’re new to Framer X (or to React), then this tutorial is a great introduction to working with code.

  • We’ll take it slow, explaining each step as we go.
  • At the end of the article, we’ll walk back through each part of the finished component code.

If you’d prefer to explore for yourself, you can download the finished project and hack away. You can also jump directly to Part II and Part III.

What are we making?

In this article, we’ll be designing a Switch, a user interface element used to turn some property on or off.

Chances are you’re familiar with the Switch pattern already — it is one of the most common elements of a mobile interface. While the component’s mechanics are pretty simple, a Switch is still a tricky element to design.

A well-designed switch should communicate:

  • that the switch has two mutually exclusive states, on or off
  • which state the switch is currently in
  • that the user can interact with the switch
  • that a change has occurred to the switch’s state

As we put together our component, the code we write will fall into two categories: controlling the logic behind the Switch’s state — whether the switch should be on or off — and controlling the presentation of that state to the user.

Setting up

Let’s get started in Framer X. If you don’t have it already, download a copy and enjoy the 14-Day trial.

  1. Open up the Framer X app.
  2. Update to the latest version, if prompted.
  3. Start a new project.

Creating a device Frame.

The first thing we’ll do is create a Frame where our component will live.

  1. Select Tool > Frame in the Menu (or press F on your keyboard)
  2. Select a Device from the panel that appears on the right side of the screen.

Selecting a device will automatically create a Frame on the canvas set size of that device’s screen. Later, when we preview our Frame, we’ll see our design as if it were displayed on that device’s screen.

Creating our code component

Next, we’ll actually create our code component.

  1. From the left sidebar, click the code tab.
  2. At the top of the empty files list, click the New button.
  3. Enter a name for our new component.
  4. Click New Code Component to create the component.
Left: creating our Code Component from the code tab. Right: our Code Component in the components tab.

Creating a Switch instance.

Once you’ve created a component, you’ll be taken to that component’s code. However, before we jump into the code, let’s create an instance of our component on the canvas.

Code components work like design components — or symbols if you’re coming from Sketch. While we only define our component once, we can create as many instances of it as we want.

  1. From the left sidebar, click the Components tab (the four boxes). Our new component should be sitting at the top of the components list.
  2. To create an instance, click the component and drag it onto the Frame that we’ve just made.
An instance of our component sitting in a device frame on the canvas.

Previewing our Frame

We’ll want to see our component as we work on it, so let’s also set up our Preview window.

  1. Click on the Device Frame
  2. Open the Preview by clicking the play button (or right-facing triangle) in the top-right corner. You can also press Command + P on your keyboard.
  3. Click the lock icon at the top of the Preview window to lock the preview to this Frame.
  4. Position your windows so that we can see our Preview window and Framer’s main window at the same time.
Your windows should look something like this. Left: the Preview window. Right: Framer’s main window.

Cleaning out the boilerplate

Now that our windows are set up, let’s go back to our component’s code.

  1. On the canvas, select our component instance (the big blue rectangle).
  2. Click the Edit Code button on the properties panel.

We want to start almost from scratch, so clear out the component’s existing code until it looks like this:

import * as React from "react"
import { Frame } from "framer"


export function Switch() {
return (
<Frame size="100%"/>
)
}

We’re all set up! We can finally — but cozily!— start writing our component.

Our component’s state

Like all React components, our Switch will respond to two sources of data: props and state.

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

State is data that the component manages internally. To reuse our pizza example, we could think of the pizza’s temperature as part of its state, changing over time without any new external input.

We’ll work with props in Part II of this series. For now, let’s focus on the component’s state.

Creating the component’s state

Our component’s state is data that the component manages itself. In our case, we’ll want our component’s state to keep track of whether the Switch is on or off.

  1. At the top of our component, create a useState hook.
  2. Define the hook’s initial state as an object with a single property, isOn, set as false.
  3. Destructure out the state variable and the setState function, as shown below.

For this project, we’ll use a boolean property, isOn, to track whether the switch is “on” or “off”. We can think of this property as a statement — “The Switch is on” — which will either be true if the Switch is on or false if it isn’t.

export function Switch(props) {
const [state, setState] = React.useState({
isOn: false,
})


return (
<Frame size="100%"/>
)
}

Flipping the Switch

Our Switch should know how to flip, so let’s add that routine.

  1. Below our useState hook, create a function named flipSwitch.
  2. In this function, use our hook’s setState function to set a new state.
  3. In the new state, define the isOn property as “not” the current isOn value, as shown below.

The ! operator will return the opposite of whatever follows it. You’ll see this a lot in JavaScript: for example, if === is “equals”, !=== is “not equals”.

export function Switch(props) {
const [state, setState] = React.useState({
isOn: false,
})


const flipSwitch = () => {
setState({
isOn: !state.isOn,
})
}


return (
<Frame size="100%"/>
)
}

Setting the tap event

Next, let’s tell our Frame to run flipSwitch when the user taps on the Frame.

  1. In the JSX that our component returns, give the Frame an onTap prop.
  2. Point this onTap prop to our flipSwitch function.

The onTap prop is one of several events that the Frame component supports. We define event props by passing the Frame a callback function. Later, if the Frame detects the event (in our case, a tap), it will call the function with information about what happened.

export function Switch(props) {
const [state, setState] = React.useState({
isOn: false,
})


const flipSwitch = () => {
setState({
isOn: !state.isOn,
})
}

return (
<Frame size="100%" onTap={flipSwitch}/>
)
}

Creating a lo-fi presentation

In order to that we can see that everything is working, let’s throw in a quick and dirty way to see the value of our state.

  1. Break the self-closing<Frame/> into “opening” and “closing” tags, <Frame> and </Frame>.
  2. Between these tags, add an expression that turns our state.isOn value from a boolean (false) into a string (“false”), so that we can display it as text.

In JSX, elements that don’t have any children will show up as self-closing, like <Frame height="200"/> . Elements that do have children will need both an opening and closing tag, like <Frame height="200"> and </Frame>, with the children in between. Note that we only set props on the element’s opening tag.

export function Switch(props) {
const [state, setState] = React.useState({
isOn: false,
})


const flipSwitch = () => {
setState({
isOn: !state.isOn,
})
}

return (
<Frame size="100%" onTap={flipSwitch}>
{ state.isOn.toString() }
</Frame>
)
}

Our state is working! If we click on our component, we can see the state.isOn switching between true and false.

Presenting our State

Now that we have our state worked out, let’s move on to presenting this state to our user. Our requirements call for much more than the blue box that we currently have.

Though both examples present the same state, the second does it better for humans.

Styling our container Frame

Our component will have two Frames — a round “knob” and a “container” in which the knob will move. We’ll use our current Frame as the container.

  1. In our JSX, set the container Frame’s height and width props with a more appropriate size.
  2. To center the container Frame, add the center prop.
  3. Round the corners by using the radius prop. To get the pill shape we want, set this prop to half of the container Frame’s height.
return (
<Frame
height={50}
width={80}
radius={25}
center={true}

onTap={flipSwitch}
>
{state.isOn.toString()}
</Frame>
)

Creating our knob Frame

Next up, let’s replace our placeholder state text with a second Frame for the component’s knob.

  1. Replace the {state.isOn.toString()} line with a new Frame instance.
  2. Set this Frame’s height to just less than the height of the container.
  3. Set the top and left props to give this Frame a margin.
  4. Set the radius prop to "100%", which will make this Frame into a circle.
  5. Set the background prop to a different color (white in my examples), so that we can see it better.

Framer X uses the same color definitions as HTML and CSS. In the example below, we’re using a hex code to define our color. Learn more about different ways of representing color in your code.

return (
<Frame
height={50}
width={80}
center
radius={25}

onTap={flipSwitch}
>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
/>
</Frame>

)

If all’s gone well, we should have something looking like this:

Creating our variants

In the last step, we removed the line {state.isOn.toString()}. Up to now, this has been our only way of knowing whether our Switch was “on” or “off”, so removing it has left us in the dark. If we click on the Switch now, nothing happens — at least, nothing we can see.

In order to represent this state visually again, we’ll create two different variants for our knob and container.

A variant is a group of properties that define a visual state for a Frames. A Frame can only be in one variant at a time, but it can switch between variants at different times.

Defining our container’s variants

Let’s start by defining the variants on our component’s container Frame. We want the container to be grey when the Switch is off and blue when the switch is on. We’ll use two variants, each of which will define one of these visual states.

  1. Set the variants prop of the container’s Frame to an object with two object properties, on and off.
  2. Set the off variant to a grey background.
  3. Set the on variant to a blue background.
return (
<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" },
}}

>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
/>
</Frame>
)

Setting our knob variants

Next, create a set of variants for the knob. We want the knob to be at the left side when the Switch is off, and the right side when the switch is on.

  1. Set the variants prop of the knob’s Frame to an object with two object properties, on and off.
  2. Set the off variant to an x of 0.
  3. Set the on variant to an x of 30.

If you’re feeling math-y, we’re calculating 30 by taking the container’s width, subtracting the knob’s width, then subtracting twice the knob’s horizontal margin, or its left prop.

return (
<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" },
}}
>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
variants={{
off: { x: 0 },
on: { x: 30 },
}}

/>
</Frame>
)

Setting our initial variant

To start with, let’s use state.isOn to determine the component’s initial variant.

  1. Set the container Frame’s initial prop to an expression that will crunch to either "on" or "off", depending on the value of state.isOn.

In the example below, we’re using JavaScript’s ternary operator. You’ll see the ternary operator a lot in JSX code: an expression a ? x : y will evaluate to x if a is true, or else y if a is false.

return (
<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" },
}}
initial={state.isOn ? "on" : "off"}
>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
variants={{
off: { x: 0 },
on: { x: 30 },
}}
/>
</Frame>
)

If all’s gone right, we should be able to test by changing our component’s initial state. Back in our component’s useState hook, change { isOn: false } to { isOn: true } and see what happens.

Notice that our knob Frame is also switching its variant to either on or off, just like the container Frame, even though we haven’t set its initial prop. Unless specifically given a variant of their own, children will “inherit” their parent’s current variant name. Learn more.

Animating to the right Variant

Once we have our initial variant set, we’ll use a different prop, animate, to define which variant the component should use when the user flips the switch.

  1. Set the container Frame’s animate prop to an expression that will crunch to either "on" or "off", depending on the value of state.isOn.

The initial prop defines which variant the Frame should use for its first render. The animate prop defines which variant the Frame should use for every other render. (Remember that a component will render once when it first loads and then re-render each time its state or props change).

return (
<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" },
}}
initial={state.isOn ? "on" : "off"}
animate={state.isOn ? "on" : "off"}

>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
variants={{
off: { x: 0 },
on: { x: 30 },
}}
/>
</Frame>
)

Great! Our component now knows two things:

  • which variant to show when the component first loads
  • how to animate between variants when the component changes its state

Tweaking the transition

Before we’re done, we should dial in that animation — a springy knob doesn’t just really feel right. We can adjust every aspect of a Frame’s animation by using a transition.

  1. Set the container Frame’s transition prop to an object.
  2. Add a type property with the value "tween".
  3. Add a duration property with the value 0.2, or one-fifth of a second.
  4. Copy this transition prop to the knob Frame, too.

There are two main types of transitions, tween and spring, each of which is defined by a different set of properties.

return (
<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
variants={{
off: { background: "#BBBBBB" },
on: { background: "#0070DF" },
}}
initial={props.isOn ? "on" : "off"}
animate={state.isOn ? "on" : "off"}
transition={{
type: "tween",
duration: 0.2,
}}

>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
variants={{
off: { x: 0 },
on: { x: 30 },
}}
transition={{
type: "tween",
duration: 0.2,
}}

/>
</Frame>
)

That feels better. Feel free to keep tweaking the design and its animation until it matches your own tastes, but I’m calling this one done!

Our finished component

Let’s take another look at our component. We’ll read it over and then go through point by point of how it works. If you’re lost about what any of the code is doing, here’s your chance to catch up.

Lines 1 – 2: At the top of the file, we import the entire React library, as well as all of the elements we need from the “framer” library.

Lines 4 – 50: Here’s where we create our component itself. The component is a function that returns JSX.

Lines 5 – 7: We use React’s useState hook to define our component’s initial state. It gives us back our state and a function useState that we can use to update that state. We use state.isOn to track whether the Switch is on or off.

Lines 9 – 13: Here we create a function flipSwitch to update our state when the user flips our switch. Inside of that function, we call useState and pass in a new state with a flipped value for state.isOn.

Lines 15 – 49: This is all JSX that our component returns. That JSX is made up of two React elements:

  • Lines 16 — 48: a Frame for our Switch’s container
  • Lines 29 — 37: a Frame as our Switch’s knob

Line 21: Here we set an event so that will run flipSwitch when the user taps on the container Frame.

Lines 22 — 25: We define our container Frame’s visual states using a pair of variants named "on" and "off".

Line 26: Next, we tell our container Frame to start in the "on" variant if the component’s initial state.isOn is true, otherwise to start in the "off" variant.

Line 27: We also tell our container Frame to animate to a different state, using the same logic as in the previous step, for any future renders. (The Frame will render each time its state changes.)

Lines 28 — 31: Here we define our animation’s transition, a tween animation that takes .2 seconds to complete.

Lines 39 — 42: We also define two variants on our knob Frame. Because this Frame is the child of our container, the changes we make to the container’s variant (using the initial and animate props on lines 26 and 27) will also set the knob Frame’s variant.

Lines 43 — 46: We also define a transition for our knob’s animation.

To download the finished project, click here.

Next Step: Props

While our component works, it does have some limitations. You may have thought of these already:

  • What if I want some Switch instances to start on but others to start off?
  • What if I want to disable the Switch?
  • How can I use the Switch to control other components?
  • How can I use other components to control the Switch?

In the next tutorial, we’ll set up props for our Switch, so that we can control it from outside. In the third and final part, we’ll learn how we can alert the rest of the project when our Switch’s state changes.

Continue to Part II.

If you liked this article, smash that clap button! 👏👏👏

--

--