In the following section, we give you a basic overview of Framer, as well as a few simple pointers on how to set yourself up for success.
Framer is a great way to get familiar with code. To that end, we’ve added many features designed to lower the learning curve. From simplified language and auto-correct to intuitive error messaging and in-app documentation, Framer guides you as you go. All of these features form the basis of our most revolutionary feature yet — Auto-Code. It’s as simple as a drag and drop interface yet powerful enough to write your code for you. Auto-Code helps you start building without the pressure of needing to know the nuances of our syntax.
That’s a high level overview of Framer. As you progress, keep in mind that every new thing you learn will exponentially increase your understanding and ability to express ideas. As a bonus, any programming concepts you pick up will be applicable to other languages. The best part is that there are simply no limitations to what you can make in Framer, so you’ll likely never stop learning. Our most advanced users (and even our internal designers) discover new ways to use Framer every day.
In this section, we go over a typical way most designers use Framer in their workflow.
As a design tool, Framer was built for prototyping. That is, the process of creating a design for validating a concept or an idea. In essence, it’s a tool for adding animations and interactions to your static designs. Here’s how most designers integrate Framer into their workflow.
From there, import into Framer and start building your prototype. Here’s where you start adding interaction and animation to your static designs. You can go back to your graphics tool and make changes as often as you like, syncing with Framer as you go. Continue reading about animation, states and events below.
As you design, you can view your prototype on real devices or upload to Cloud to generate a share link. This allows you to get feedback from real users or team members and iterate on your prototype until it’s perfect.
That’s the basic premise of how Framer works. The rest of the guide breaks down core concepts and gives you the working knowledge — as well as reusable code snippets — to start using Framer today.
If you’ve ever used a visual design tool, you’ll already be familiar with the concept of a Layer. Without content, it’s simply a rectangle. But it can contain an image, a video, audio, text, and much more.
Layers have many types of properties. Ones that define its position, appearance, and even interactivity. Every layer has a set of default properties. It has a width and height of 200 by 200, and a transparent grey background.
All of these default properties can be overridden. For example, you can define a blue background, and give your layer a solid white background color.
# Background layerbg = new BackgroundLayerbackgroundColor: "#28affa"# Create a layerlayerA = new Layerx: 0y: 0width: 200height: 200backgroundColor: "#fff"
A layer has a ton of visual properties. You can transform it, scale it, hide it and more. After having created a layer, you can still override its properties.
# Override propertieslayerB.borderRadius = 4layerB.rotation = 45layerB.opacity = 0.5layerB.scale = 0.5
A great way to explore all layer properties is by using AutoCode. In front of every layer you create, an edit icon appears. Click this to bring up the properties panel. Here, you can directly manipulate your layers. For a list of all layer properties, check out our Docs.
A layer can be positioned with its x and y properties. These values define the distance from the top-left corner of the canvas.
# Position layerAlayerA.x = 200layerA.y = 200
Layers can also be positioned using dynamic properties.
Say you want to position one layer relative to another.
Instead of manually calculating the center point of this layer, you can simply use the
You can find all positioning properties in our Docs.
layerA = new Layerx: 200y: 200layerB = new Layeropacity: 0.5# Align to the center of layerAlayerB.x = layerA.midXlayerB.y = layerA.midY
You can center a layer within its parent by using the
Layers can also be exclusively centered horizontally or vertically.
See below for all available Align properties.
- Align.left (x)
- Align.right (x)
- Align.top (y)
- Align.bottom (y)
- Align.center (x and y)
# Parent layerlayerA = new Layerwidth: 400height: 400# Align to the bottom-right cornerlayerB = new Layerparent: layerAx: Align.righty: Align.bottom
Layers can be grouped. A layer placed within another layer is called a
child layer. The container layer is called the
Layers inherit properties from their parent layers, like their opacity or position.
# Two ways to define hierarchylayerB.parent = layerAlayerA.addChild(layerB)
If you want to place a layer in front of another layer, you can use the
placeBefore method. Vice versa, you can use
layerA = new LayerlayerB = new Layer# Place layerB on toplayerB.placeBefore(layerA)
Layers can be almost anything. Think backgrounds, images, videos, text and more. To add an image, for example, you simply reference an image located in /images within your project folder.
# Imageimage = new Layerimage: "images/sea.png"# Videovideo = new Layervideo: "fish.mp4"
Text layers have unique text styling properties. The width and height is automatically calculated and set, depending on the text and its style.
# Create text layertitle = new TextLayertext: "Hello!"
Most of the text styling properties are inherited from CSS. You can find all of these properties and more in our Docs.
# Create text layertitle = new TextLayertext: "Hello!"fontSize: 64fontWeight: 600x: Align.centery: Align.center
Almost every layer property can be animated. Multiple properties can be animated at once. You can then continue to define the curve, time, delay and many more custom animation options.
Let’s start by animating the opacity of a layer. You can start animations by using the
animate keyword, and then defining a set of properties.
You can completely customize the feel of your animation, by changing options like time, curve and delay. Below is a list of the available options:
- time (in seconds)
- curve (Bezier, Spring)
- delay (in seconds)
- repeat (amount of times)
- colorModel (rgb, hsl, husl)
Durations in Framer are defined in seconds.
Add animation options by using the
options keyword, and indenting its properties.
# Animate with an easing curvelayerA.animaterotation: 180borderRadius: 200options:curve: Bezier.easetime: 1
Animations optionally take a curve that describe the type of animation.
You can use pre-defined curves like
Bezier.ease curve is the default in Framer. See all built-in easing curves below, or check out this website to learn more.
# Animate with an easing curvelayerA.animatescale: 0.75options:curve: Bezier.easedelay: 0time: 1
To make animations that feel truly native, you’ll want to work with spring curves. The bounciness of our spring curve can be customized with
damping and it can be used along with the
# Animate with a spring curvelayerA.animatescale: 0.75options:curve: Spring(damping: 0.5)time: 0.5
States allow you to define the different appearance options of a layer. A layer can have multiple states, each with a different set of properties. States can then be cycled through, with or without animation.
Think of states as a different way to manage multiple animations. You start by adding the states, and then animate or cycle betweem them.
In the example below, the default state of layerA contains the opacity property, plus all default layer properties. Next, a new state for layerA is defined, titled "fade", with an opacity set to 0.
# The default statelayerA = new Layeropacity: 1# A new state titled "fade"layerA.states.fade =opacity: 0
For layerA to inherit the properties of the "fade" state, it needs to switch from the default state to the "fade" state.
# Animate to the statelayerA.animate("fade")# Instantly switch to the statelayerA.stateSwitch("fade")
States can also be cycled between, using
Just like with layer animations, you can also add animation options to states.
# Add states and animation optionslayerA.states.rotate =rotation: 0animationOptions:time: 1curve: Bezier.ease# Cycle between stateslayerA.onTap ->layerA.stateCycle()
To override states, simply add a new state with the same name.
# Override third statelayerA.states.rotate =rotation: 180
Events are things that can happen to layers, often triggered by user interaction. With events, you can animate layers based on these interactions. From simple taps and swipes to advanced multi-touch gestures.
Framer includes a ton of events.
The most common ones you may be familiar with, like
onScroll and like.
Events can also be caused by animations, state switches, page changes and the like.
And if you want to design more complex interactions, you can use the multi-touch gestures, like
layerA.onTap ->...layerA.onScroll ->...layerA.onSwipe ->...layerA.onAnimationEnd ->...
One of the most common use cases of events is toggling between a set of states on tap (which is click or touch).
# Toggle states on tapsettings.onTap ->settings.stateCycle()
Example: Animation Chaining
Events can be used to chain animations.
For example, you can start a new animation after another one ends by listening to the
# Animation EventslayerA.onAnimationStart ->...layerA.onAnimationEnd ->...
Below is a simple example of a chained animation. Every animation gets an AnimationEnd event, so they can be chained infinitely.
layerA.animatex: 80options:curve: Bezier.easelayerA.onAnimationEnd ->layerA.animatex: 0options:curve: Bezier.ease
Draggable layers include physics and many customizable properties that open up new creative possibilities. By accounting for things like the speed and the direction, you can take greater control over dragging interactions.
Let’s start by creating a draggable layer. Simply set
true. Now, you can pick it up, place it elsewhere, and toss it around.
You can also restrict the dragging direction, by disabling dragging horizontally or vertically. Both are enabled by default. You can also specify the speed. This provides accelerated or reduced dragging.
# Make the layer draggablelayerA.draggable.enabled = true# Prevent vertical dragginglayerA.draggable.horizontal = truelayerA.draggable.vertical = false# Alternative way by setting the speedlayerA.draggable.speedX = 1layerA.draggable.speedY = 0
In most cases, you’ll want to limit the range a layer can be dragged within. For example, think of the pull-to-refresh gesture, in which you can only drag a certain distance. This can be achieved with constraints.
x, y, width and height properties. Think of it as another layer, that contains your draggable layer.
# Make the layer draggablelayerA.draggable.enabled = true# Set the constraints framelayerA.draggable.constraints =x: 0y: 0width: 160height: 80
Overdrag, Bounce and Momentum
It’s likely you’re already familiar with these terms, even if you don’t know them by name. Let’s have a look at each of them.
A draggable layer can be dragged beyond its constraints, although it will snap back. This is called
overdrag. Think of Safari on iOS, where you can drag websites beyond the top and bottom of the pages.
When a layer is moving in a direction, it can also bounce beyond its constraints. This is called
bounce. It’s sort of like a rubber-band effect.
momentum to false disables the default physics of your draggable layer. You can still move it, but you can’t toss it around anymore. The momentum and bounce properties can also be customised. Have a look at this example of a custom draggable layer with constraints.
# Disable overdraglayerA.draggable.overdrag = false# Disable bouncelayerA.draggable.bounce = false# Disable momentumlayerA.draggable.momentum = false
The three fundamental dragging events that you can listen to are:
DragEnd. The Move event is fired whenever the draggable layer is moving. Unlike the DragMove event, this includes the momentum and bounce animations.
# Start dragginglayerA.onDragStart ->layerA.animatescale: 1.1# After dragginglayerA.onDragEnd ->layerA.animatescale: 1
There are two specific events that occur when momentum and bounce are enabled:
DragAnimationDidEnd. They occur after DragEnd, while the layer is animating.
# After DragEnd, the DragAnimation startslayerA.onDragAnimationStart ->layerA.animatescale: 0.8# Starts with the momentum and bouncelayerA.onDragAnimationEnd ->layerA.animatescale: 1
Pinchable layers can be scaled and rotated with two fingers. This multi-touch gesture is often seen in maps and photo apps, to zoom and navigation content.
In Framer for Mac, you can hold the
alt key while moving your cursor to bring up a second cursor. This allows you to emulate multi-touch gestures.
Just like with draggable layers, you can enable pinching by setting
layerA = new LayerlayerA.pinchable.enabled = true
Pinchable layers contain
rotate properties, both enabled by default.
By disabling the scale, you can only rotate a pinchable layer, and vice versa.
# Disable scale on pinchlayerA.pinchable.scale = false# Disable rotation on pinchlayerA.pinchable.rotate = false
The three basic pinchable events are:
Let’s have a look at the last one. Pinching adjusts the actual scale and rotation properties of a layer.
So after a pinch, we can animate these properties back to their default values.
# Enable pinchinglayerA.pinchable.enabled = true# Animate back to original positionlayerA.onPinchEnd ->layerA.animatescale: 1rotation: 0options:curve: Spring(damping: 0.5)time: 0.5
If you enable both dragging and pinching, the layer also becomes pannable.
Panning is the same as dragging, except it’s a multi-touch gesture.
You can detect a pan with the
# Enable panninglayerA.draggable.enabled = truelayerA.pinchable.enabled = true# Animate back to original positionlayerA.onDragEnd ->layerA.animatescale: 1rotation: 0options:curve: Spring(damping: 0.5)time: 0.5
Think of a component as a bundle of layers, with pre-defined interactions. Like the SliderComponent, which allows you to add sliders without having to create all the elements from scratch. They’re completely customizable, but work out of the box.
The FlowComponent helps you quickly transition between multiple screens. It’s especially useful for turning static screens into fully interactive flows.
What’s unique about the FlowComponent is that it knows which screen you’re viewing. It keeps track of your history, allowing you to navigate back-and-forth at any time. By default it features detailed, native-feeling transitions. When switching screens, the current screen automatically animates out of view. And when adding an overlay like a modal dialog, the background gets a semi-transparant dark overlay, too.
Lets start by adding a layer to the FlowComponent. This works much like adding layers to the PageComponent, and can easily be done by using the
showNext function. The first layer you add doesn’t get animated.
# Create layerlayerA = new Layer# Create FlowComponent, show layerAflow = new FlowComponentflow.showNext(layerA)
You can use the
showNext function to transition to the next screen.
# Create layerslayerA = new LayerlayerB = new Layer# Create FlowComponent, show layerflow = new FlowComponentflow.showNext(layerA)# Switch screens on clicklayerA.onClick ->flow.showNext(layerB)
Framer keeps track of which screen you’re currently viewing. You can switch back to the previous screen with the
# Switch to next screenlayerA.onClick ->flow.showNext(layerB)# Switch to previous screenlayerB.onClick ->flow.showPrevious()
With one of the
showOverlay functions, you can overlay any layer. The current screen gets a semi-transparent dark background. This is most noticable when overlaying a smaller layer, like a sidebar or an action sheet. You can specify one of the following directions:
# Create FlowComponentflow = new FlowComponent# Modal and button layersbutton = new Layermodal = new Layer# Switch backbutton.onClick ->flow.showOverlayCenter(modal)
Any child layer that exceeds the height or width of the FlowComponent automatically becomes scrollable. It will be vertically scrollable if it’s taller, and horizontally scrollable if it’s wider.
To add a fixed layer to a scrollable FlowComponent, like a tab bar or a navigation bar, you can use the
# Set a fixed navigation bar (on top)flow.header = flowBar# Set a fixed tab bar (on bottom)flow.footer = tabBar
You’re probably familiar with the smooth scrolling on iOS. Getting this to feel just right involves a lot of physics. The ScrollComponent in Framer takes care of this for you, while remaining completely customizable.
A ScrollComponent is built with two layers. First, the ScrollComponent itself, which serves as a masking layer.
Second, the content layer, which has
draggable enabled and pre-defined constraints. The size of the content layer is calculated for you, based on the size of its children.
# Create a ScrollComponentscroll = new ScrollComponentsize: 120# Create the content layerslayerA = new Layerparent: scroll.contentlayerB = new Layerparent: scroll.content
Similar to draggable layers, you can also restrict the scrolling direction.
scroll.scrollHorizontal = truescroll.scrollVertical = false
A great way to bring your static designs to life with Framer is by using the Import feature. To add scrolling behavior to imported layers, you can wrap them in a ScrollComponent, using
# Sketch Importsketch = Framer.Importer.load("imported/scrollable@1x")# Wrap the imported content layersscroll = ScrollComponent.wrap(sketch.content)
To add some extra spacing to your content, you can use the
contentInset property. This is useful to place content within a scrollable area. Think of a header on top a list. You can’t scroll beyond the header, but you can scroll the feed. An example of this can be seen in Twitter for iOS.
# Create a ScrollComponentscroll = new ScrollComponentwidth: 120height: 120scrollHorizontal: false# Define the contentInsetscroll.contentInset =top: 40bottom: 40right: 0left: 0
The three fundamental scrolling events are:
Scroll (or ScrollMove) and
ScrollEnd. Within the Scroll event, the positions of the scrollable layer can be retrieved. For example, animations can be started based on the vertical scrolling distance with
# Create a ScrollComponentscroll = new ScrollComponentscrollHorizontal: false# Listen to the Scroll eventscroll.onScroll ->if scroll.scrollY < -10layerA.animatescale: 1
Just like with draggable layers, there are two specific events that occur when momentum and bounce are enabled:
ScrollAnimationDidEnd. They occur after ScrollEnd.
# After Scroll, the ScrollAnimation startsscroll.onScrollAnimationDidStart ->layer.animatewidth: 100# After the scroll animationscroll.onScrollAnimationDidEnd ->layer.animatewidth: 120
With the PageComponent, piecing together multiple static screens into a single, interactive prototype is a breeze. It handles all of the calculations, allowing you to focus on the experience.
The PageComponent allows you to easily swipe between layers, in any direction. It’s based on the ScrollComponent.
# Create a PageComponentpage = new PageComponent# Create page layerslayerA = new Layerparent: page.contentlayerB = new Layerx: 110parent: page.content
New pages can be added by using
page.addPage(). This takes a layer, and places it on the right side. Pages can also be added on the bottom.
# Create a PageComponentpage = new PageComponent# Create a set of layerslayerA = new LayerlayerB = new Layer# Add layerA to the right# Add layerB to the bottompage.addPage(layerA)page.addPage(layerB, "bottom")
You can automatically generate a set amount of pages using a for-loop. Below, we create 10 new layers, and place each within the page.content. Finally, we set the x property by multiplying the
index variable by 105.
This places each layer to the right, with 5 pixels of spacing.
# Create a PageComponentpage = new PageComponentscrollVertical: false# Create 10 layersfor index in [0...10]layer = new Layerparent: page.contentsize: 200x: 210 * index
To add pagination behavior to imported layers, you can wrap them in a PageComponent, using
# Sketch Importsketch = Framer.Importer.load("imported/pages@1x")# Wrap the imported content layerspage = PageComponent.wrap(sketch.content)
Ordering and Sorting
Detecting a page-switch can be done with the
change:currentPage event. This is really useful when designing things like page indicators.
# Listen to any page switchpage.on "change:currentPage", ->page.previousPage.animateopacity: 0.2scale: 0.8page.currentPage.animateopacity: 1scale: 1
You don’t always want to start at the first page. You can also automatically snap to a page layer, with or without an animation.
# Snap to the layer pageThreepage.snapToPage(pageThree)# Snap with a custom animation curvepage.snapToPage(pageTwo, true, curve: Spring)
You can keep track of the index of your pages, measured either horizontally or vertically. The index is the page number, minus 1. This means the index will always start at 0 instead of 1. To highlight many of the mentioned properties and methods, have a look at this Page Indicator example.
page.on "change:currentPage", ->for layer in indicatorslayer.animateopacity: 0.5current = page.currentPagei = page.horizontalPageIndex(current)indicators[i].animateopacity: 1
Sliders can be used to show progress, change the volume, adjust photos, define a price range and more. With the SliderComponent, you can build adaptable designs without having to start from scratch.
The SliderComponent consists of 3 layers: the slider itself, the fill and the knob.
The slider is track of the slider. The knob is used to change the
value of the slider.
fill represents the current value.
You can change the appearance of these layers, just like any other layer.
The SliderComponent has a few unique properties:
- min (minimum value)
- max (maximum value)
- value (starting value)
- knobSize (width and height of the knob)
# Create Sliderslider = new SliderComponentmin: 0max: 100value: 50knobSize: 40# Customize fill colorslider.fill.backgroundColor = "#fff"
The slider itself is simply a Framer layer, meaning you can change all of its visual properties. Same thing goes for the fill and knob layers.
# Customize sliderslider.backgroundColor = "#DDD"# Customize fillslider.fill.backgroundColor = "#00AAFF"# Customize knobslider.knob.shadowY = 2
onValueChange function, you can detect value changes, and retrieve the current value as it’s being changed.
# Create Sliderslider = new SliderComponentmin: 0max: 100value: 50# Get the current valueslider.onValueChange ->print slider.value