Archive » Overriding Code Components

Overriding Code Components

With an override, you can dynamically change a code component’s properties, and by overriding its events, you can have it talk to other elements on the canvas.

 

📘 The Framer book – 🖍 Code overrides – 🧩 Code components

Public packages

These examples use components from different public packages (of the legacy kind) that are not available in newly created Framer Sites projects. (If you don’t see public packages in the Insert Menu, check Preferences > Show Packages.)

Which properties can you override?

Code components (almost always) have property controls for their custom properties, but these don’t necessarily show each property’s actual name. A property control might say ‘Text color’ while the prop’s name is defaultTextColor. But there’s a simple way to know the correct prop names: The ‘Code’ section in the right-hand panel will list a component’s properties, also the props whose controls might be temporarily hidden.

One thing might be missing, though: events. Components that don’t have a connector on the canvas will not list their events under Code.

Framer’s Default button | Button from Framer Base Kit

For example, Framer’s default button has a connector, but the button from the (older) Framer Base Kit package hasn’t.

Components with a connector

Framer’s Default Components all have connectors, and components from recently updated public packages might have them as well. The technical reason? They have event handler property controls that tell Framer to which events they’ll react.

Here’s Framer’s default button again:

The button on the canvas 👆
and its properties in Handoff 👉

Event names almost always start with ‘on’, so this button has onClick(), onMouseEnter(), and onMouseLeave().

As you know, you can drag the connector to create a Transition (archived version) to a different screen, one of the possible Interactions. That’s why you’ll also see these same three events as interaction triggers in the component’s Interactions panel (top of the Properties Panel).

A component’s events (triggers) will often be user gestures, e.g., onClick(), onMouseEnter(), etc. But a video player component might have onPlay() and onEnd() triggers (that let you know when the video started and stopped), and Framer’s Loading Indicator has an onTimeout() trigger.

Not only components have interaction triggers, frames and stacks also have them.

The components in Carbon Design System, an implementation of IBM’s design system by Framer’s Iain Kettles, also have connectors.

A few of Framer’s Default Components and a selection from Carbon Design System

So with components like these, it’s easy to know which events you can use in an override. But if you also want to know which values an event returns, you’ll need to test for it with an override.

Button example

As an example of using overrides with a code component, we’ll make Framer’s default button toggle-able.

Here the button’s onClick() event flips the value of toggled in the data store:

const useStore = createStore({ toggled: false })

export function FramerDefaultButton(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                text={store.toggled ? "Great tap 🤟!" : "Tap me !"}
                defaultBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.defaultBackground
                }
                hoverBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.hoverBackground
                }
                pressedBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.pressedBackground
                }
                onClick={() => setStore({ toggled: !store.toggled })}
            />
        )
    }
}

The current state of toggled (true or false) defines the button’s text and its background color:

const useStore = createStore({ toggled: false })

export function FramerDefaultButton(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                text={store.toggled ? "Great tap 🤟!" : "Tap me !"}
                defaultBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.defaultBackground
                }
                hoverBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.hoverBackground
                }
                pressedBackground={
                    store.toggled
                        ? "Coral"
                        : props.children.props.pressedBackground
                }
                onClick={() => setStore({ toggled: !store.toggled })}
            />
        )
    }
}

This button has a prop for its defaultBackground color and separate ones for the color when hovered over or pressed. The override changes all three to “Coral” when toggled is true.

When toggled is false, the colors all default to the values picked with their property controls, just like would happen without this override. The values set by the property controls are in the props.children.props object (same as with smart components).

Unpacking properties

Pro tip: You can use JavaScript’s destructuring assignment to unpack the properties you need from props. This way, you don’t have to write props.children.props… everywhere.

export function FramerDefaultButton_UnpackProps(Component): ComponentType {
    return (props) => {
        const { defaultBackground, hoverBackground, pressedBackground } = props
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                text={store.toggled ? "Great tap 🤟!" : "Tap me !"}
                defaultBackground={store.toggled ? "Coral" : defaultBackground}
                hoverBackground={store.toggled ? "Coral" : hoverBackground}
                pressedBackground={store.toggled ? "Coral" : pressedBackground}
                onClick={() => setStore({ toggled: !store.toggled })}
            />
        )
    }
}
You can find this version of the override in the same file.

Components without a connector

Like this button from the Framer Base Kit package, many older components don’t list their events in the Handoff panel. When a component doesn’t have event handler property controls, Framer has no way of telling to which events it might respond.

The Framer Base Kit button on the canvas
and its properties in Handoff 👉
Some more components from the Framer Base Kit package

Events hunt

So, how do you figure out which events to use with these components?

onWhoKnows() ?

1. Check the docs

I would first peruse the package’s description in the Insert Menu. The component’s creator might have listed all its props, including event callback properties.

When the component is part of an existing UI kit, wrapped for use in Framer, you can check the original documentation. For example, that’s how I learned that the Material-UI slider (docs) not only has an onChange(), but also an onChangeCommitted() event, called when you release its knob.

2. Test for common events

The second option: Use an override to listen for popular events.

This override checks for four common events, onClick(), onTap(), onChange(), and onValueChange() by logging a message to the Console.

export function CheckCallback(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                onClick={() => console.log(`${props.name} : onClick() works`)}
                onTap={() => console.log(`${props.name} : onTap() works`)}
                onChange={() => console.log(`${props.name} : onChange() works`)}
                onValueChange={() =>
                    console.log(`${props.name} : onValueChange() works`)
                }
            />
        )
    }
}

We don’t know which events a component might react to, but these make sense for the following reasons:

  • onClick() is the standard click event used on the web (actually a ‘mouse event’, but it also works on touch devices);
  • onTap() is a common Framer event;
  • onChange() is the event emitted by form elements on the web, like text fields, radio buttons, checkboxes, etc.;
  • onValueChange() is just an often used event name for input elements.

Still, the component’s creator could have used completely different event names. I’ve seen, for instance: onCheck() and onUnCheck() on a checkbox, onToggle() for switches, and onChangeDate() for a date picker.

Pro tip: How to open the console is a bit hidden. Check the preview’s hamburger menu in the top left corner, or type ⌘I (Windows: Ctrl+I).

Example: Framer Base Kit

I did this override test with a few elements from Framer Base Kit. You could consider the components in this package the predecessors of Framer’s current default components.

Open the console to see to which events these components react.
  • The ‘Button’ and ‘Icon’ emit an onClick();
  • the ‘Toggle’ (💙), ‘Input’, ‘Switch’, and ‘Slider’ have both onChange() and onValueChange() events;
  • the ‘RadioGroup’ emits an onClick() when you tap the already selected option, plus adds onChange() and onValueChange() when you change its selection.

3. Dive in the code

When nothing was documented, you can look at the component’s code. That’s how I found out that Fernando Hurtado’s Data Stack has an onItemTapped() callback event that is triggered when you tap a row.

…
export type Props = {
  component: any;
  width?: number;
  height?: number;
  direction: "horizontal" | "vertical";
  expandChildren: boolean;
  onItemTapped: (index: number, record: any) => void;
  limit: number;
  debug: boolean;
  scrollable: boolean;
} & SourceProps &
  StackProperties;
…

You might find event names in the component’s TypeScript Props object (as above), its defaultProps, or in the list of unpacked properties at the beginning of its code.

Button example

In this example, we’ll make it possible to flip the state of the Framer Base Kit button, making it act like a toggle.

Here’s another look at this button’s Handoff props:

The Framer Base Kit button

No events are listed, but we now know that tapping it triggers an onClick(), which we’ll use to change store.toggled to its opposite value.

const useStore = createStore({ toggled: false })

export function FramerBaseKitButton(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                text={store.toggled ? "Great tap 🤟!" : "Tap me !"}
                intent={
                    store.toggled ? "success" : props.children.props.intent
                }
                onClick={() => setStore({ toggled: !store.toggled })}
            />
        )
    }
}

When toggled is true, the button will have "Great tap 🤟!" as its text and "success" as its intent.

const useStore = createStore({ toggled: false })

export function FramerBaseKitButton(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                text={store.toggled ? "Great tap 🤟!" : "Tap me !"}
                intent={
                    store.toggled ? "success" : props.children.props.intent
                }
                onClick={() => setStore({ toggled: !store.toggled })}
            />
        )
    }
}

Initially, or whenever toggled is false, the button will say "Tap me !" and have the intent you gave it on the canvas.

Pro tip: The Intent property control doesn’t show which string it uses behind the scenes ("success"), but you can select an option with the property control and then check the resulting value under Code in the right-hand panel.

Testing which values an event returns

Sometimes you’ll want more information than just when the component was tapped or clicked. You might want to know what the component’s current state is.

  • With a checkbox or switch: Was it flipped on or off?
  • When it’s a slider: What’s its current value?

Sometimes there’s only one way to get this information from the component: through an event that returns a value.

Components with a connector

When a component has a connector, it will list its events in the Handoff panel. To figure out which information they return, you can log these events’ arguments to the console.

I first noted down the event names of four default components: Input, Toggle, Radio Buttons, and the Slider:

  • Input: onFocus(), onChange(), onSubmit(), and onBlur()
  • Toggle: onToggle(), onToggleOn(), and onToggleOff()
  • Radio Buttons: onChange(), onOption1Tap(), onOption2Tap(), etc. up to onOption10Tap()
  • Slider: onChange(), onMax(), and onMin()

…and then gave each component a special override that prints out the returned values:

The console logs for each component

Here’s the override for the Toggle, with onToggle(), onToggleOn(), and onToggleOff():

export function CheckToggle(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                onToggle={(...args) => {
                    console.log(`${props.name} : onToggle()`)

                    args.map((arg, index) => {
                        console.log("  " + index + `: ${arg}`)
                    })
                }}
                onToggleOn={(...args) => {
                    console.log(`${props.name} : onToggleOn()`)

                    args.map((arg, index) => {
                        console.log("  " + index + `: ${arg}`)
                    })
                }}
                onToggleOff={(...args) => {
                    console.log(`${props.name} : onToggleOff()`)

                    args.map((arg, index) => {
                        console.log("  " + index + `: ${arg}`)
                    })
                }}
            />
        )
    }
}

There’s no way to know how many arguments each event will return, so I map() through all of them (after putting them in an args array with the ... rest parameter).

export function CheckToggle(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                onToggle={(...args) => {
                    console.log(`${props.name} : onToggle()`)

                    args.map((arg, index) => {
                        console.log("  " + index + `: ${arg}`)
                    })
                }}
                …

The onToggle() will log something like this to the Console:

It returns two objects, plus a Boolean telling whether you toggled it on or off.

To know what those objects contain, use console.log() to only print out the object, like this:

export function CheckToggle(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                onToggle={(...args) => {
                    console.log(`${props.name} : onToggle()`)

                    args.map((arg, index) => {
                        console.log(arg)
                    })
                }}
                …

You’ll see them appear with a little ▶ signaling you can open them to view their contents:

Components without a connector

With other components, we’ll again have to guess, check their docs, or take a look at their code. However, UI controls (input, toggle, switch, slider,…) will often use onValueChange() or onChange() to return their values. Here’s another investigative override with these two events:

export function CheckReturnedValues(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                onChange={(...args) => {
                    console.log(`${props.name} : onChange()`)

                    if (typeof args[0] === "object" && args[0] !== null) {
                        console.log(
                            "  event.target.value: " + args[0].target.value
                        )
                        console.log(
                            "  event.target.checked: " + args[0].target.checked
                        )
                    } else {
                        console.log("  0: " + event)
                    }

                    if (args[1]) {
                        console.log("  1: " + args[1])
                    }
                }}
                onValueChange={(...args) => {
                    console.log(`${props.name} : onValueChange()`)

                    args.map((arg, index) => {
                        console.log("  " + index + `: ${arg}`)
                    })
                }}
            />
        )
    }
}

The onChange() event

An HTML <input> element (like a textfield, checkbox, radiobutton,…) will emit an onChange() event.

This event returns an event object that contains a target (a reference to the HTML element itself). And this target object will contain (among other things) the current value (string or number), or, in the case of a checkbox or radio button, a checked value (Boolean).

The above override prints out both event.target.value and event.target.checked. And when the first argument isn’t an event object, it’ll print whatever else was returned. Plus: it also looks for a potential second argument (args[1]).

The onValueChange() event

The onValueChange() event is not part of any official API, not even Framer’s, but it’s a name often used by component creators.

The event might return anything, even an entire list of values. That’s why I map() through all the arguments. (I’ve put them into an args array with the ... rest parameter.)

Let’s test!

Example: Framer Base Kit

Here again, a few controls from Framer Base Kit.

The components react to both onChange() and onValueChange()

These input elements seem to follow web standards because they all have an onChange() that returns either an event.target.value or event.target.checked, depending on the type of input. But their creator(s) also equipped them with an onValueChange() that returns just the value.

Examples

Slider + Arconic

Let’s grab the default Slider and use it to control the arc of Benjamin den Boer’s Arconic component.

These components’ properties, as listed by Handoff:

<Slider
  overdrag={false}
  {/* Using default values */}
  background={"#DDD"}
  color={"#FFF"}
  fillColor={"#09F"}
  knobSize={25}
  max={100}
  min={0}
  onChange={undefined}
  onMax={undefined}
  onMin={undefined}
  shadow={"rgba(0,0,0,0.1)"}
  track={4}
  value={50}
/>
<Arconic
  strokeWidth={50}
  {/* Using default values */}
  color={"#0EF"}
  colorEnd={"#60F"}
  length={360}
/>

I made two overrides:

  • Input() gets the slider’s current value through its onChange() callback event,
  • and Output() sets the length of the arc.
const useStore = createStore({ sliderValue: 180 })

export function Input(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                onChange={(value) => setStore({ sliderValue: value * 3.6 })}
            />
        )
    }
}

export function Output(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return <Component {...props} length={store.sliderValue} />
    }
}

The first override, Input(), saves the returned value in store.sliderValue. (Multiplied by 3.6 because I want to convert the slider’s 0–100 range to the 0–360 degrees of the arc.)

And by binding the Arconic’s length property to store.sliderValue, the arc will always follow the slider.

Radio Buttons + Charts

Here we use Framer’s Radio Buttons to change the properties of one of the Charts in Kirill Morozov’s package.

The Radio Buttons’ onChange() event changes the chart’s data and color, and the color of the radio button itself.

Chart data

But let me first show you the data:

const barChart = {
    names: ["Hotel", "Mini", "Dive", "Sports", "Music", "Karaoke"],
    values: [1830, 2420, 204, 3010, 3360, 274],
    color: "Crimson",
}
const pieChart = {
    names: ["Apple", "Cherry", "Pumpkin", "Pecan", "Rhubarb", "Peach"],
    values: [772, 189, 179, 55, 16, 97],
    color: "Brown",
}
const bubbleChart = {
    names: ["Bath", "Tea", "Gum", "Stock market", "Sort"],
    values: [181, 335, 169, 402, 263],
    color: "ForestGreen",
}

const useStore = createStore({ selectedChart: 0 })

The data used for each chart never changes, so I made a separate const object for each chart with its names and values arrays, and its color.

The currently selectedChart does change, so I made this one a variable in the data store.

Override for the radio buttons

Here’s the RadioButtons() override. The onChange() event returns the selected option as an index number (the first button being 0), which I save in store.selectedChart.

export function RadioButtons(Component): ComponentType {
    return (props) => {
        const { activeColor, textActiveColor } = props
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                onChange={(value) => setStore({ selectedChart: value })}
                activeColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : activeColor
                }
                textActiveColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : textActiveColor
                }
            />
        )
    }
}

Override for the chart

The override for the Chart() changes a bunch of properties:

  • the chart’s barsNames and barsValues arrays,
  • the color of the bars: barColor,
  • the color of the lighter background after each bar: bgColor,
  • and the color of the horizontal and vertical text labels: xAxisColor and yAxisColor.
export function Chart(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                barsNames={
                    store.selectedChart === 1
                        ? barChart.names
                        : store.selectedChart === 2
                        ? pieChart.names
                        : store.selectedChart === 3
                        ? bubbleChart.names
                        : props.children.props.barsNames
                }
                barsValues={
                    store.selectedChart === 1
                        ? barChart.values
                        : store.selectedChart === 2
                        ? pieChart.values
                        : store.selectedChart === 3
                        ? bubbleChart.values
                        : props.children.props.barsValues
                }
                barColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : props.children.props.barColor
                }
                bgColor={Color.toString(
                    Color.alpha(
                        Color(
                            store.selectedChart === 1
                                ? barChart.color
                                : store.selectedChart === 2
                                ? pieChart.color
                                : store.selectedChart === 3
                                ? bubbleChart.color
                                : props.children.props.barColor
                        ),
                        0.25
                    )
                )}
                xAxisColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : props.children.props.xAxisColor
                }
                yAxisColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : props.children.props.yAxisColor
                }
            />
        )
    }
}

(The bgColor prop needs a lighter version of the color, which I get by creating a Framer Color() object and changing its alpha() to 0.25, after which I use toString() to get a CSS rgb() string.)

When selectedChart is equal to 1, 2, or 3, I use the corresponding value inside barChart, pieChart, or bubbleChart. In the ‘else’ case, when selectedChart is 0, I use the value set by the property control inside props.children.props.

As you probably noticed: I’m also changing the Radio Buttons’ activeColor (color of the selected 🔘) and its textActiveColor.

export function RadioButtons(Component): ComponentType {
    return (props) => {
        const { activeColor, textActiveColor } = props
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                onChange={(value) => setStore({ selectedChart: value })}
                activeColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : activeColor
                }
                textActiveColor={
                    store.selectedChart === 1
                        ? barChart.color
                        : store.selectedChart === 2
                        ? pieChart.color
                        : store.selectedChart === 3
                        ? bubbleChart.color
                        : textActiveColor
                }
            />
        )
    }
}

(That color change doesn’t happen instantly, probably because the button and text label were animated to the current color.)

By the way, these charts don’t display random values. This is hard-core data 📊. They show how many millions of Google search results there are for each search term.

More examples

The Smart Components section has a few more examples that use the values returned by my iOS segmented control component.

Even more properties

When a component was made with <Frame>s or Motion elements, you might be able to pass it Framer properties, including animation props and events.

Take this FABButton and PrimaryButton from the Elements UI Kit by Todd Reynolds:

These are the buttons’ ‘official’ properties:

<FABButton
  {/* Using default values */}
  name={"Add"}
  onTap={undefined}
  tint={"#4af196"}
/>
<PrimaryButton
  {/* Using default values */}
  buttonType={"a"}
  label={"Hello Framer!"}
  onTap={undefined}
  tint={"#0099ff"}
/>

What’s particular about the components in this package, and what I recommend every component should do, is that they accept incoming properties and pass them to the container frame (or Motion element).

Here’s a snippet from FABButton’s code. It unpacks the component’s custom props (text, tint, onTap) and passes the rest to the <Frame>.

export function FABButton(props) {
    const { text, tint, onTap, ...rest } = props

    return (
        <Frame
            {...rest}
            borderRadius={28}
            top={0}
            left={0}
            style={{
                WebkitBackdropFilter: "blur(6px)",
                backdropFilter: "blur(6px)",
                backgroundColor: "rgba(0,0,0,0)",
            }}
        >
            …

This means that any other property you pass it will be passed on to the frame, just like happens when you override a frame drawn on the canvas.

The component doesn’t have a property control for its background color, but it contains a <Frame>, so I can set its backgroundColor directly. And I can give it some ‘while’ animations.

export function Buttons(Component): ComponentType {
    return (props) => {
        return (
            <Component
                {...props}
                tint="White"
                backgroundColor="Tomato"
                whileHover={{ backgroundColor: "Red" }}
                whileTap={{ backgroundColor: "DarkRed" }}
            />
        )
    }
}

It’s hard to tell if a component was constructed like this without looking at the code. So an easy way to figure this out is by doing a small test, like trying to make it draggable:

export function Draggable(Component): ComponentType {
    return (props) => {
        return <Component {...props} drag />
    }
}

My iOS Segmented Control is another one that does this.

This component also accepts other Framer properties, like here onHoverStart() and onHoverEnd().

export function SegmentedControl(Component): ComponentType {
    return (props) => {
        const [hover, setHover] = useState(false)

        return (
            <Component
                {...props}
                onHoverStart={() => setHover(true)}
                onHoverEnd={() => setHover(false)}
                options={
                    hover
                        ? ["🍒", "🥝", "🍌", "🥭", "🍋"]
                        : props.children["props"].options
                }
            />
        )
    }
}

Which options the segmented control now shows depends on its hover state. When hovered over, it will show the emojis; and when not, the options set by the property control.

Just wrap it

When a component doesn’t support a prop or event you’d like to use, you can often just wrap it in a frame and apply an override to that frame.

Here’s how to do a similar ‘change options on hover’ trick with Framer’s Segmented Control.

This control’s ‘Track Color’ was set to 0% opacity. Thus, the gray (or orange) background you see is that of the frame wrapping it.

The parent frame has the below Wrapper() override applied. A whileHover animates the frame’s backgroundColor to “Coral”, and the onHoverStart() and onHoverEnd() events flip the hover variable in the data store.

const useStore = createStore({ hover: false })

export function Wrapper(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                onHoverStart={() => setStore({ hover: true })}
                onHoverEnd={() => setStore({ hover: false })}
                whileHover={{ backgroundColor: "Coral" }}
            />
        )
    }
}

This store.hover is then used to toggle the segments and fontSize of the segmented control:

export function SegmentedControl(Component): ComponentType {
    return (props) => {
        const [store, setStore] = useStore()

        return (
            <Component
                {...props}
                segments={
                    store.hover
                        ? ["🍒", "🥝", "🍌", "🥭", "🍋"]
                        : props.children.props.segments
                }
                fontSize={store.hover ? 22 : props.children.props.fontSize}
            />
        )
    }
}

14 comments on “Overriding Code Components”

  • lpetkov says:

    Hi! I can’t figure out how to listen for value changes for the components in this design system: https://github.com/EMCECS/clarity-react which I’m trying to integrate within Framer X.

    I tried your approach, but to no avail with neither the Toggle component or the checkbox and others, I only got the Input to work. I can see that e.g. the original Checkbox component has an onChange prop, but somehow I can’t get its value.

    Could you have a look and see what I’m missing? Thanks! Here’s my Framer file: https://send.firefox.com/download/4d594eb17bc02d5a/#TRG8S2ukXrVd1PxZzL6cYQ

    • Tes Mat says:

      I took a look at that design system’s checkbox and noticed that it doesn’t share its state.

      It does have an internal value state, which gets toggled by this handleChange() function (triggered by its onChange()):

      private handleChange(evt: React.ChangeEvent) {
          const newValue = !this.state.value;
          this.setState({
              value: newValue,
          });
      }

      … but the current value is not sent back upstream. It’s not shared through the onChange() callback event.

      When you pass it an onChange(), it will call it (when onChange !== undefined see below), but then its internal handleChange() isn’t called anymore (so the checkbox stays unchecked).

      <input
          type="checkbox" // prettier
          id={setId ? setId : id}
          name={name}
          ref={this.myRef}
          checked={checked !== undefined ? checked : value}
          onChange={onChange !== undefined ? onChange : this.handleChange.bind(this)}
          className={ClassNames.CLR_CHECKBOX}
          aria-labelledby={ariaLabelledby}
          aria-label={ariaLabel}
          disabled={disabled}
          data-qa={dataqa}
          onClick={onClick}
      />

      This means that this component is meant to be used as a controlled component. Its parent has to keep track of its state.

      So in your wrapper component I added a checked state:

      export function Checkbox(props) {
          const { itemsArray, onValueChange } = props
      
          // We’re keeping the state of the checkbox here
          const [checked, setChecked] = React.useState(false)
      
          …

      … which gets passed to the component:

          <CheckBox_
              label={itemsArray[index]}
              checked={checked} // current state is passed as a prop
              onChange={handleToggle} // passing our function that updates the state (and shares the new value)
          />

      Together with a handleToggle() function that updates that state and also shares it through onValueChange():

      export function Checkbox(props) {
          const { itemsArray, onValueChange } = props
      
          // We’re keeping the state of the checkbox here
          const [checked, setChecked] = React.useState(false)
      
          // This is called by the CheckBox_’s onChange()
          function handleToggle() {
              const newValue = !checked
              // Sharing the new value
              onValueChange(newValue)
              // Setting the new value
              setChecked(newValue)
          }
          …

      (I used onValueChange() because an onChange() normally also returns the event object.)

      See the attachment for this updated Checkbox wrapper. (For Framer Beta, because your project was also updated for beta.)

  • Evan says:

    Say I have an override that (successfully) changes a certain property to a design component, like changing its position from the top of the viewport dynamically.

    I’ve now replaced that design component with a code component. Using the same override as above, my prototype works just fine (i.e. my code components are overridable).

    But I don’t want to use overrides. I want to include all the behavior inside my code component. My thought has been to move my override function inside the code component and use an event (onLoad?) to fire it, just like I did before with the override function. This doesn’t seem to be working (the property doesn’t change). Any idea what event should I look for to instantly change the property of a code component if I wanted to avoid using overrides?

    • Tes Mat says:

      What did the override do? Did it change the component’s position all the time (and with which kind of event?), or just once, when the prototype loads?

      Whatever you did in the override when the prototype loads, you should be able to do it inside the component as well, if you place that code before the component’s return() statement.

      When animating upon load, it’s even simpler, the component then doesn’t need any logic (or state), just an animate, like this:

      import * as React from "react"
      import { Frame } from "framer"
      
      export function MoveDownUponLoad() {
          return (
              <Frame animate={{ y: 200 }} />
          )
      }

      About properties: A component cannot change its own props. Props are always send down to it by a parent component or an override. (More about this and the difference between ‘props’ and ‘state’.)

      To get rid of overrides or parent components that control a component (by sending it props), you often just need to make the component, sort of… bigger. Take the 25. Colors: Switching between states project as an example.

      The version with overrides has two overrides. One for the white box and another one for the background. So the overrides have the ‘state’ (in that Data object) and send down the animate properties to those frames (and also those hover events).

      But the code component version is just one big component that contains everything. It has the state (now a useState()), contains both frames, and controls them by sending them the correct properties. (A frame is actually also a component, but a dumb one, like a design component.)

  • danbillingham says:

    Alright – this is all beginning to make sense – but I have a problem with the opacity not working here. Can you see anything wrong with this?

    My code component…

    import * as React from “react”
    import { Frame, addPropertyControls, ControlType } from “framer”

    export function Controls(props) {
    return (

    )
    }

    Controls.defaultProps = {
    radius: 25,
    opacity: 1,
    scale: 1,
    x: 0,
    rotate: 50,
    }

    addPropertyControls(Controls, {
    radius: { type: ControlType.Number, title: “Radius” },
    })

    —–
    My override…

    import * as React from “react”
    import { Override, Data, Color, motionValue, useTransform } from “framer”

    const scrollY = motionValue(0)

    export function Scroll(): Override {
    return {
    contentOffsetY: scrollY,
    }
    }

    export function PageHeader(props): Override {
    const newOpacity = useTransform(scrollY, [0, -50], [1, 0.5])
    const newScale = useTransform(scrollY, [0, -50], [2, 0.6])
    const x = useTransform(scrollY, [0, -50], [300, -200])
    const newRotate = useTransform(scrollY, [0, -50], [0, 360])
    return {
    opacity: newOpacity,
    scale: newScale,
    x,
    rotate: newRotate,
    }
    }
    —-
    ??

  • danbillingham says:

    Legend! Thanks for all your assistance on this. Top man : )

  • arron says:

    Really am enjoying, working through the Book, .. much appreciated Tes…

    I have been using charts in my prototypes so was more than happy to see an example of the bar chart that was using overrides to dynamically push the data … For the life of me, I cannot figure out how to override the ‘Scatter’ chart using overrides … // Is there something different I should be doing different here with multiple sets and multiple values to override this particular chart?

    Any direction, much appreciated!

    • Tes Mat says:

      Hi!

      It seems to work more or less the same… only with different properties. I made a simple example.

      One thing tripped me up: I noticed you can enter the data for not one but three scatter charts (you pick which data set you want to enter with the last property control: ‘Current set’).

      So I thought you could switch between three displayable sets (maybe with a better animation)… but it’s different: you can display only the first set, or both the first and second set, or all three sets. (You make this selection with the first property control: ‘Sets’)

      Hope this helps!

      • arron says:

        This is absolutely awesome my friend. I was so close :/ the current set is where I was tripping up. Although the Framer community is growing, there is limited knowledge available when it comes to great examples using code… Happy I found this gem of site.

Leave a Reply