Framer X » Overrides

Overrides

Overrides (sometimes also called code overrides) are a way to quickly make elements on your canvas interactive. With Overrides you can animate Frames and Components, and have them react to events like a tap, hover, drag, etc.

In this page, I’ll explain what the bits of code in an Override actually do. We’ll start with a very basic Override, one that merely changes the color of a Frame to red:

export const ChangeColor: Override = () => {
  return {
    background: "red"
  };
};

… and then we’ll add some code so that it changes color when tapped (example 2). Then we’ll have it animate (example 3). And lastly: we’ll combine what we learned to have it animate when tapped (example 4).

download the example project

Applying an Override to a Frame

You probably already tried Framer’s example Overrides. If not, here’s how you connect an Override to a Frame:

  • With the Frame on the canvas selected, open the ‘Override’ tab in the properties panel;
  • In the tab, you select which ‘File’ you want to use (there will be a preinstalled one with ‘Examples’);
  • And in the second menu, ‘Override,’ you then pick one of the Overrides inside that file.

Now open the Preview window to test it. (Overrides only work in preview.)

Editing Override files

Click the ‘Edit Code’ button in the Override tab to jump to Framer’s built-in editor. You can simply add your own Overrides to the included ‘Examples.tsx’ file.

Or you select ‘New File…’ (in the tab’s ‘File’ menu) to create a fresh ‘App.tsx’ file (which will also contain an example override).

You can add as many files if you want, and each file can contain many overrides, but you can only apply one Override at a time to any Frame.

What are you ‘overriding’?

With an override, you can dynamically change the properties of a Frame or Component.

Any property that you can set in the Properties Panel can be changed (and often also animated) on the fly. So you can change a Frame’s size and position, but also its fill, border, shadow, etc.

This overriding happens between the Canvas and the Preview. Which means that the result of an override will only be visible in the Preview window.

Back to our simple example.:

On the Canvas
In the Preview window

This ChangeColor override just changes the background of the Frame you attach it to. That’s all, no animation, there’s not even a tap needed to trigger it.

It’s the plainest possible override function.

import { Override } from "framer";

export const ChangeColor: Override = () => {
  return {
    background: "red"
  };
};

Whatever the Frame’s Fill was on the Canvas (this can be a color, gradient or image) will be overridden. The Frame will simply be "red".

Here’s what happens in the code above:

  • First, we import Override from the Framer library.
  • Then we create an ES6 Arrow function (=>),
  • that we save in a const variable with the name of ChangeColor,
  • while giving it the (TypeScript) type of Override.
  • Our function returns a JavaScript object which contains only one key-value pair: the key is background with a value of "red".
  • Ah, and we make sure to export our function.

Okay, step by step, in more detail.

Importing another JavaScript file

import { Override } from "framer";

We import Override from the Framer library so that we can create an Override function. That’s also all we need from that library, for now.

Importing is like the script src statement you would use to add a JavaScript library to your webpage, like, e.g., the jQuery library.

<script src="js/jquery-3.3.1.min.js"></script>

An ES6 Arrow function

In JavaScript, you can write functions in a few different ways, and we’ll use the newest one.

Here’s an overview, just for reference:

A function declaration

The add function below is written as a function declaration. The function accepts two numbers and returns their sum.

function add(number1, number2) {
  return number1 + number2
}

You would use it like this (try it in a Codepen here):

add(4,5);

So when you type the above line in that Codepen’s console (at the bottom of the screen), the function will return:

> 9

A function expression

But can also write functions like this, in what they call a function expression:

add = function(number1, number2) {
  return number1 + number2
}

Note how the name, add, and the function keyword switched place.

An arrow function

But we’ll use an arrow function, something new in ES6, which—with this same add example—looks like this:

add = (number1, number2) => {
  return number1 + number2
}

Note that there’s no more function keyword. The arrow itself (=>) makes it clear that this is a function.

(ES6 refers to version 6 of the ECMA Script programming language. ECMA Script is the official name for JavaScript, and version 6 was finalized in June 2015. You might also see it being referred to as ‘ECMAScript 2015’)

And when a function doesn’t need any input values, like our ChangeColor example, you can leave the parentheses empty. (Try this example on Codepen.)

TheAnswer = () => {
  return 42;
}

By the way, when you call the function, you always have to write the parentheses.

TheAnswer();

> 42

So, back to our Override. We were writing this ChangeColor function. Here it is again, now as ‘just a simple arrow function’:

ChangeColor = () => {
  return {
    background: "red"
  };
};

And what does our function do? It returns the properties we want to change on the Frame.

Our function returns an object

The updated properties are returned as an object.

{
  background: "red"
}

A JavaScript object can contain as many properties as you want, and they always go in key and value pairs. Here the key is background with "red" as the value.

This means that you could, for instance, also rotate the frame:

ChangeColor = () => {
  return {
    background: "red",
    rotate: 45
  };
};

Our function is saved in a const

Our ChangeColor variable, which contains the function, is a const. This stands for ‘constant,’ a variable that cannot be changed afterward.

const ChangeColor = () => {
  return {
    background: "red"
  };
};

The other option in ES6 is let, and those variables can be changed

(let is like the new version of what used to be var in old school JavaScript, a variable that can be changed afterward.)

It’s always better to use const, though, because the editor will warn you when you (accidentally) try to use the same variable name for something else.

Here I try to save a text string in our ChangeColor variable:

ChangeColor = "A text string, for example.";

Which results in the editor putting a squiggly red line under the variable to signal an error.

Hovering over the error brings up more information. Here: “Cannot assign to ‘ChangeColor’ because it is a constant or read-only property.”.

A bit more about const

To be honest, const isn’t always a synonym for ‘immutable.’ It depends on what you’ve put inside that ‘const’ variable.

When it’s a String, Number, Boolean, or in our case, a Function, then it’s true that the value can’t be ‘mutated.’

But when the variable contains an Array or Object, then the values inside that Array or Object can be changed.

const justAnObject = {
  text: "I’m a text string"
};

justAnObject.text = "I’m now a different text string.";

More info: Use const and make your JavaScript code better

Our function is typed as an Override

In Framer Classic we used CoffeeScript, a language built on top of JavaScript that was easier to use and looked cleaner (no curly braces).

In Framer X we use TypeScript.

While CoffeeScript had a different syntax and looked different from JavaScript, TypeScript is JavaScript… with something extra.

People use TypeScript because it lets them give variables a predefined shape, a type (hence the name of the language).

You can define, for instance, that a variable should always contain a String, or a Number, or a Boolean.

Say you have a (let) variable called totalPrice, with a type of number, that initially contains 0:

let totalPrice: number = 0;

When you then change it to another numeric value…

// updating the price
totalPrice = 100;

… everything will work fine.

But when you try to set it to a String…

totalPrice = "one hundred bucks";

… the editor will complain.

You can also define which types of parameters a function will accept. Remember our simple addition function from higher up?

We can say that the number1 and number2 parameters it accepts should always be numbers.

const addNumbers = (number1: number, number2: number) => {
  return number1 + number2
}

Back to our ChangeColor function. We give our function the type of Override.

const ChangeColor: Override = () => {
  return {
    background: "red"
  };
};

This enables the editor to check if our code is kosher because it knows what an Override can and should contain. (That’s what we imported higher up: all the details about this Override type.)

Setting the type to Override also makes the function discoverable. Only functions of this type will appear in the menu inside the Override tab. (And, by extension, only files that contain Override functions will show up in that tab.)

We export the function

By writing export before defining the function, you make it so that Framer can import it and display it in the Override tab.

export const ChangeColor: Override = () => {
  return {
    background: "red"
  };
};

So if you’re writing an Override and it doesn’t show up in the menu, one of the following statements will be true:

  1. You forgot to type it as an Override (easy to solve)
  2. You forgot to export the function (piece of cake)
  3. Your function has an error

Errors are harder to fix, but any mistake in your code will be underlined with a scribbly red line. Hover over it for more information.

Example 2: Tap to change the color

Finally, some interaction!

On the Canvas
In the Preview

import { Override, Data } from "framer";

const data = Data({
  colorA: "#66BB66" // A shade of green
});

export const ChangeColorOnTap: Override = () => {
  return {
    background: data.colorA,
    onTap() {
      data.colorA = "red";
    }
  };
};

That’s a lot more code.

We’re now using Framer’s Data object, in which we save the current color of the background: colorA. When the frame is tapped, we indirectly change that color by changing data.colorA.

There’s now this back-and-forth going on: the Frame’s background is set to colorA inside the data object, and by changing it to "red" in the onTap() event handler we change it to the background as well.

The Data object

We’re now also importing Framer’s Data object.

import { Override, Data } from "framer";

We create an instance of it (named data), in which we put a colorA value with the initial color: "#66BB66".

const data = Data({
  colorA: "#66BB66"
});

The value that we’re changing has to be inside an instance of Framer’s Data object because this will trigger React to redraw the screen.

Then, inside our return statement, we attach data.colorA to the background property:

export const ChangeColorOnTap: Override = () => {
  return {
    background: data.colorA,
    …

Whenever we change data.colorA, it will, in turn, change the background, because they’re bound together.

We also bind an onTap() event handler to the Frame.

By the way, events are like messages from Framer saying, “Hey, this just happened!” They will often be user actions, like this onTap() or an onScroll(), etc.

    onTap() {
      data.colorA = "red";
    }

When the Frame is tapped, data.colorA will now be set to "red".

Why this back-and-forth with the Data object? It’s because React has to know that something changed so that it can update the screen. The Date object is tied into React and will notify it when one of its values changes.

If onTap() is a function, then where is its arrow? Yes, this is also a function, even without the arrow or a function keyword. Remember that our return statement returns an object. A function inside an object is called a method and can be written in this simplified form.

Example 3: Animating the color

On the Canvas
In the Preview
export const AnimateColor: Override = () => {
  return {
    animate: { background: "red" },
    transition: { duration: 1 }
  };
};

Since the launch of the new APIs, every Frame now has an animate property. When you give animate an object with properties and values, it will animate to these values.

    animate: { background: "red" }

You can pass most properties to animate. (Check out the list of Animatable properties.)

So, just like in our earlier example, we could also rotate the Frame. Both animations would happen simultaneously.

    animate: { background: "red", rotate: 45 },

And there’s also the (optional) transition property to which you pass animation options like the length of the animation, which curve to use, an optional delay, etc.

export const AnimateColor: Override = () => {
  return {
    animate: { background: "red" },
    transition: { duration: 1 }
  };
};

Here I gave it a duration of 1 second because the default speed was rather fast.

In short:

  • in animate you put what you want to animate;
  • in transition you put how you want to animate it

Example 4: Animating the color when tapped

On the Canvas
In the Preview
const data = Data({
  colorA: "#66BB66", // A shade of green
  colorB: "#66BB66" // ditto
});

…

export const AnimateColorOnTap: Override = () => {
  return {
    animate: { background: data.colorB },
    transition: { duration: 1 },
    onTap() {
      data.colorB = "red";
    }
  };
};

Now we bring the two together. We animate the background when the Frame is tapped.

When you give a Frame’s animate a set of properties, it will animate them, every time. But it will also animate instantly, the moment you give it different values.

To animate to a value not instantaneously, but later, when the frame is tapped, we give animate a value that’s inside the Data object. This way we can trigger the animation when we would like to.

Here we give it a value for the Frame’s background, but it’s one that’s in the Data object: data.colorB. It contains the same green color.

    animate: { background: data.colorB },

Now, when we change data.colorB in the onTap()event handler, this will trigger an animation to the new background color.

    onTap() {
      data.colorB = "red";
    }

(And when you change it back, it would also animate back, always with the same duration.)


2 comments on “Overrides”

  • jbrick says:

    Hi Tes!

    I notice that in example 4 the frame animates to the green color when the prototype is run, and then animates to red when tapped. Is there a way to prevent the initial animation from happening?

    I found that I can do this to disguise the fact that the animation happens before the tap:

    export const AnimateColorOnTap: Override = () => {
    return {
    background: data.colorA,
    animate: { background: data.colorB },

    But is there a way to avoid any animation until the frame is tapped?

    • Tes says:

      Hi,
      Did you maybe change the color of the fourth Frame on the Canvas? To another shade of green? Because they should be the same.

      But, it should be possible (in theory) to set data.colorB to the current background color of the Frame, like I did here (but without the set() because we’re not using Animatables).

      Only one problem: I couldn’t figure out how to use it with props.background, because that property can contain different values (a color, or an image, gradient)

      This, for instance, does not work:

      export const AnimateColorOnTap: Override = props => {
        data.colorB = props.background;
        return {
          animate: { background: data.colorB },
          transition: { duration: 1 },
          onTap() {
            data.colorB = Color("red");
          }
        };
      };

      … because it says that props.background is of type Background and can’t be assigned to a humble string like data.colorB.

      (I also tried to use Color values, but that didn’t work either.)

Leave a Reply