Framer X » Overrides » More complicated overrides

More complicated overrides

download the example project

Only setting a property

This is the super simple example from the former chapter, it only changes the background property of the frame by directly setting it to "red".

On the Canvas
In the Preview

import { Override } from "framer";

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

Setting a property when tapped

Finally, some interaction!

On the Canvas
In the Preview

import { Override, Animatable } from "framer";

const colorValue = Animatable("#66BB66");

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

We animate values, not properties

Now we get to touch on something important.

In Framer, you don’t animate a property directly. You animate a value that you attached to a specific property.

You might say: “But we’re not even animating!” True, but we are changing the color. It starts off as green, #66BB66, and then we change it to red.

So we first create a variable to hold the color, colorValue, that we set to the initial green color, "#66BB66":

const colorValue = Animatable("#66BB66");

Then, inside our return statement, we attach colorValue to the background property:

    background: colorValue,

Now, whenever we change colorValue, it will, in turn, change the background, because they’re bound together.

We also bind an onTap() handler to the frame.

    onTap() {
      colorValue.set("red");
    }

So that when it is tapped colorValue will be set to "red".

Again, directly setting background will not work, you have to change the value that you tied to the background property.

Something like this (trying to set the property directly) does not work:

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

The Animatable object

You noticed that we’re using this Animatable object, which we’re now also importing from the Framer library.

import { Override, Animatable } from "framer";

That value you want to change (or animate) should be inside an Animatable.

const colorValue = Animatable("#66BB66");

Framer will notify React of changes to an Animatable object so that React knows when to update (re-render) the screen.

(By the way, the variable can be a const because the object itself will not change, only the value we’re storing inside it.)

And to change the value, we use the Animatable’s set() function:

colorValue.set("red");

Believe me, I tried, using a simple let variable instead of an Animatable, like this…

let colorValue = "#66BB66";

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

… will not work.

Animating a property when tapped

On the Canvas
In the Preview

import { Override, Animatable, animate } from "framer";

const colorValue = Animatable("#66BB66");

export const AnimateColorOnTap: Override = () => {
  return {
    background: colorValue,
    onTap() {
      animate(colorValue, "red");
    }
  };
};

We need the animate function, so we add it to our imports.

import { Override, Animatable, animate } from "framer";

But anyway, if you type animate in your code without having it imported, VS Code will offer to add it to the import statement. (A little light bulb will pop up when you click the error.)

Obviously, the value should be an Animatable, so that we can animate it.

const colorValue = Animatable("#66BB66");

And again, we don’t animate the property itself (background) but the value that we attached to it:

      animate(colorValue, "red");

We animate colorValue to a new value: "red".

We didn’t give animate more more details, so this animation will use the default settings: an ‘ease’ curve with a duration of one second.

Back to Framer’s example ‘Scale’ override

And now, we can take another look at that example that’s in every new overrides file:

import { Data, animate, Override, Animatable } from "framer"

const data = Data({ scale: Animatable(1) })

export const Scale: Override = () => {
  return {
    scale: data.scale,
    onTap() {
      data.scale.set(0.6)
      animate.spring(data.scale, 1)
    },
  }
}

It’s not too difficult, is it?

The only extra thing happening here is that, when tapped, the scale of the frame is made smaller (0.6), just before it gets animated back to the original scale of 1. (Otherwise it wouldn’t animate.)

And in this one, the Animatable, named scale, is placed inside Framer’s Data object.

Animating a property automatically

On the Canvas
In the Preview

import { Override, Animatable, animate } from "framer";

const colorValue = Animatable("#66BB66");

export const AnimateColorInstantly: Override = () => {
  animate(colorValue, "red");
  return {
    background: colorValue
  };
};

You can have an animation start right away, by kicking it off before even returning the properties to the frame.

  animate(colorValue, "red");
  return {
    background: colorValue
  };

So here we start with animating colorValue, and only then we say: “By the way, this should be the value of the background. Apply it, because it’s already changing!”

(The other way round will not work, because a return is always the end of a function.)

With this technique, the frame will animate the moment it appears. (So you can use it in click-through prototypes.)

Starting from the current value of a property

By the way, we actually don’t have to set the start color, we could read the frame’s current color.

All the frame’s properties are inside a props object that we can pass in.

Look, for example, at the Fade override in Framer’s Examples.tsx :

const data = Data({
  opacity: Animatable(1)
});

export const Fade: Override = props => {
    data.opacity.set(props.opacity)

    return {
        opacity: data.opacity,
        onTap() {
            animate.linear(data.opacity, 0)
        },
    }
}

The opacity animatable is set to a start value of 1.

But, just to be sure, the override also gets the current opacity from the frame’s props.

Often an override function doesn’t need any of the frame’s existing properties so you can leave its parameters empty: ( ).

export const Fade: Override = () => {
  ...

But when you add the props object, like this:

export const Fade: Override = props => {
  ...

You can get the current value of any of the frame’s properties.

This way we can set data.opacity to the frame’s current opacity, props.opacity, before we do anything else.

    data.opacity.set(props.opacity)

13 comments on “More complicated overrides”

  • andy says:

    The example of what “= props =>” did before I read this had me banging me head for days.

  • ak says:

    How would I change a custom property in my design component, like a string? For example, I have a frame that I turned into a design component that contains text. I’m using it in my design as a button. When the button is pressed, I want the text to say something different. Animatable seems to work for colors, but what about strings?

    • tes34690 says:

      You can’t ‘animate’ a string (only change it), so you put it as a common variable inside the Data object, like this:

      const data = Data({
        text: "hi"
      });
      
      export const ChangeText: Override = () => {
        return {
          label: data.text
        };
      };

      (Here I’m changing it instantly, without a tap event)

      Important:

      • You have to use the name you set in the master (here: ‘label’)
      • And you can only override instances, not the master
  • nikhil1291 says:

    Hi Tes, will the data object be explained in detail some where else? Haven’t understand how and when to use that.

  • william.jm.harvey says:

    Very cool that we can receive the current value of the property!
    Would it be possible to receive the Parent’s Width? Also, if the width is set to Relative, is there a way to convert the Width percentage into Pixels? Im wondering how I can always hide an element off screen, by knowing the width of the component or its parent, or the entire screen width. Thanks in advance!

    • Tes says:

      No, a child’s props don’t contain any reference to its parent.

      But, you can do it the other way round: attach another override to the parent to get the details of the child.

      For instance, if a parent has only one child, this will print its width in the Console:

      import { Override } from "framer";
      
      export const ReadWidth: Override = props => {
        return {
          onTap() {
            console.log(props.children[0].props.width);
          }
        };
      };

      And in separate override, attached to the child, you could then animate it off screen.

      About converting relative width to width in pixels (points): props.width will contain a percentage, so you can just calculate it, I guess. (You might have to cut of the % at the end.)

      About screen or device size: there doesn’t seem a global object (yet) with that information, like we had in Classic. But you can always just attach an override to the device frame to get that info.

  • anton.rose says:

    Hi Tes,
    I’ve been going through the new Framer API. Very cool but I’m struggling to convert the examples into correct code to be used in an override. Is there some sort of rule of thumb to follow or does it take some coding knowledge to work it out?

    https://www.framer.com/api/examples/

    I was looking at the Cycle example – https://codesandbox.io/s/xl7nmv46wq

    • Tes says:

      Hi,
      I’m also trying to convert the examples (the ones in the file that you can download) to versions using Overrides.

      And most of them are possible and easy except the ones that use hooks like useAnimation, useCycle,.. (anything that starts with use…) because you can’t use hooks inside Overrides. (Or not yet. They are trying to add it.)

      I had to do things differently. My version of a cycle animation looks like this:

      // Cycle On Tap
      const data_Cycle_On_Tap = Data({
        current: 0
      });
      
      export const Cycle_On_Tap: Override = () => {
        const cycles = [
          { scale: 1, rotate: 0 },
          { scale: 1.5, rotate: 45 },
          { scale: 1.25, rotate: 90 }
        ];
        return {
          animate: cycles[data_Cycle_On_Tap.current],
          onTap() {
            data_Cycle_On_Tap.current =
              (data_Cycle_On_Tap.current + 1) % cycles.length;
          }
        };
      };

      So I have an cycles object with the different animation states, and I animate to the current one inside it. And that current value is inside a Data object.

      When the frame is tapped, I change current, and this triggers the animation.

    • Tes says:

      Check out this new page about using Overrides in the beta.

Leave a Reply