Creating a Switch: Part I

30m

Intermediate

One of the best ways to dive into Framer X is to learn by doing. Through this short tutorial series, you’ll learn how to create and use a code component in Framer X, a powerful capability that is unique to this design tool.

In Part I of this tutorial, you’ll learn how to:

  • Set up your Framer X workspace
  • Create a code component for your switch
  • Define your component’s state logic
  • Present your 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. It assumes no previous knowledge, takes things slow, and explains each step. At the end of the article, there’s a line by line walkthrough of the finished code and how it impacts your component.

If you’d prefer to explore for yourself, you can download the finished project and hack away.

What are you making?

In this article, you’ll be designing a Switch, a user interface element used to turn a property on or off. Chances are, you’re probably already familiar with the switch pattern — it’s 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. Overall, 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 you put together your component, the code you 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

Now let’s get started in Framer X. If you don't already have Framer X installed, go ahead and download a free 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 you’ll need to do is create a Frame to house your component. Frames are responsive and adapt when you resize, all while keeping UI elements like buttons, cards, and tab bars completely intact.

  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 you preview your Frame, you’ll see your design as if it were displayed on that device’s screen.

Creating a code component

Next, let’s create a 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.

If you’re stuck, check the image above to understand how to create a new code component from the code tab (left) and then view it in the components tab (right).

Creating a switch instance

Once you’ve created a component, you’ll be taken to the code of that component. But before jumping into the code, let’s visually create an instance of the component on the canvas.

Code components work much like design components, or symbols if you’re coming from Sketch. While you only need to define your master component once, you can create as many instances of it as you want.

  1. From the left sidebar, click the Components tab (the four boxes). Your new component should be at the top of the components list.
  2. To create an instance, click the component and drag it onto the Frame that you’ve just made.

You should now be able to view an instance of your component nested within a device frame on the canvas (above).

Previewing the Frame

You’ll likely want have realtime visual feedback of your component as you work on it, so let’s also set up your 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 Preview window should now mirror what is on the canvas in the Framer interface.

Cleaning out the boilerplate

Now that your Preview is set up, let’s go back to the code of the component.

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

You’ll 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%"/>
)
}
COPY

You’re all set up! Now you can start writing your component.

The component’s state

Like all React components, your 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. An analogy: 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 the pizza analogy, you could think of the pizza’s temperature as part of its state, changing over time without any new external input.

You’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

Your component’s state is data that the component manages itself. In this case, you’ll want your component’s state to keep track of whether the switch is on or off.

  1. At the top of your 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, you’ll use a boolean property, isOn, to track whether the switch is “on” or “off”. You 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%"/>
)
}
COPY

Flipping the switch

Your switch should know how to flip, so let’s add that routine.

  1. Below your useState hook, create a function named flipSwitch.
  2. In this function, use your 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%"/>
)
}
COPY

Setting the tap event

Next, let’s tell the 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. Let’s 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}/>
)
}
COPY

Creating a lo-fi presentation

In order to see if 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 your state.isOn value from a boolean (false) into a string (“false”), so that you 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 you 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>
)
}
COPY

Now your state is working! If we click on your component, you’ll see the state.isOn switching between true and false.

Presenting the State

Now that we have your 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.

Styling the container Frame

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

  1. In the 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.
<Frame
height={50}
width={80}
radius={25}
center={true}
onTap={flipSwitch}
>
{state.isOn.toString()}
</Frame>
COPY

Creating the knob Frame

Next up, let’s replace the 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, you’re using a hex code to define your color. Learn more about different ways of representing color in your code.

<Frame
height={50}
width={80}
center
radius={25}
onTap={flipSwitch}
>
<Frame
size={46}
top={2}
left={2}
radius={"100%"}
background="#FFFFFF"
/>
</Frame>
COPY

If all goes according to plan, you should have something looking like this:

Creating the variants

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

In order to represent this state visually again, let’s create two different variants for your 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 the 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. Let’s 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.
<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>
COPY

Setting the knob variants

Next, create a set of variants for the knob.The knob should be on 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, 30 is calculated by taking the container’s width, subtracting the knob’s width, then subtracting twice the knob’s horizontal margin, or its left prop.

<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>
COPY

Setting the 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, use 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.

<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>
COPY

If everything worked, you should be able to test by changing your component’s initial state. Back in your component’s useState hook, change { isOn: false } to { isOn: true } and see what happens.

Notice that your 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 you have our initial variant set, let’s 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).

<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>
COPY

Great! Now your component 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 finishing up, let’s dial in that animation — a springy knob just doesn’t feel quite right. You 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.

<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>
COPY

That feels better. Keep tweaking the design and its animation until you reach your desired outcome.

The finished component

Now let’s take another look at the code for our component. In this section, let’s go through it line by line to see how it all 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, the entire React library has been imported, as well as all of the elements needed from the “Framer” library.

Lines 4 – 50: Here’s where the component itself is created. The component is a function that returns JSX.

Lines 5 – 7: React’s useState hook was used to define the component’s initial state. It returns a state and function useState that can be used to update that state. State.isOn was used to track whether the switch is on or off.

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

Lines 15 – 49: This is all JSX returned by the component. That JSX is made up of two React elements:

Lines 16 - 48: A Frame for the switch’s container

Lines 29 - 37: A Frame as the switch’s knob

Line 26: Here an event is set so that it will run flipSwitch when the user taps on the container Frame.

Lines 22 - 25: The container is defined by the Frame’s visual states using a pair of variants named "on" and "off".

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

Line 27: Our container Frame is also set 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 our animation’s transition is defined, a tween animation that takes .2 seconds to complete.

Lines 39 - 42: Two variants on our knob Frame. Because this Frame is the child of the container, the changes made 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: Here the transition is defined for the knob’s animation.


Download the finished project ›

In the next tutorial: Props

While the component works, it does have some limitations and you may have considered some of the following:

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

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

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: