Standing out in the crowd

BY Radhika Maheshwari

Thursday, October 10, 2024

Comprehensive Guide to Framer Code Overrides and Code Components


Overview

Framer is a powerful visual web builder that allows users to draw elements on a canvas, which are then compiled into React components. There are two main ways to add custom functionality to your Framer projects: Code Overrides and Code Components.  

Code Overrides

Code Overrides are React higher-order components (HOCs) that allow you to modify properties of existing elements on the canvas. They are useful for adding animations, logic, and interactions to pre-existing elements.

Key Points:

  • Code Overrides are written in TypeScript.

  • They are applied to elements by selecting the file name from the ’Code Overrides’ dropdown in Framer.

  • Avoid using generics; Framer does the type checking itself.

  • Use the withFunctionName naming convention for higher-order components.

Example Code Override:

import type { ComponentType } from "react";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function withLowerOpacity(Component): ComponentType {
  return (props) => {
    return <Component {...props} opacity={0.5} />;
  };
}
Changing CSS Property with Override:
import type { ComponentType } from "react";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function ChangeColor(Component): ComponentType {
  return (props) => {
    const { style, ...rest } = props;
    return (
      <Component {...rest} style={{ ...style, backgroundColor: "Red" }} />
    );
  };
}

Code Components

Code Components are custom React components written in TypeScript that can be added to your Framer project. These components allow for more complex interactions and use of third-party libraries.

Key Points:

  • Must be a single, function-based .tsx file with inlined CSS.

  • The <Component> returned can accept motion props.

  • Use Framer's auto-sizing options where applicable.

  • Ensure props are spread correctly to support Framer's sizing options.

Example Code Component:

import { motion } from "framer-motion";
import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  const { text, color, style } = props;
  return (
    <motion.div style={{ ...style, backgroundColor: color }}>
      {text}
    </motion.div>
  );
}
MyComponent.defaultProps = {
  text: "Hello World!",
  color: "#09f",
};
addPropertyControls(MyComponent, {
  text: { type: ControlType.String, title: "Text" },
  color: { type: ControlType.Color, title: "Background Color" },
});

Writing Custom Code Overrides

Import Necessary Modules:

  • Import ComponentType from React.

Define the Override Function:

  • Use higher-order component syntax.

Spread Props Correctly:

  • Ensure all props are passed correctly to the component.

Example Override with State:

import type { ComponentType } from "react";
import { createStore } from "https://framer.com/m/framer/store.js@^1.0.0";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
const useStore = createStore({
    variant: "1",
});
export function changeVariant1(Component): ComponentType {
  return (props) => {
    const [store, setStore] = useStore();
    const changeVariant = () => {
      setStore({ variant: "1" });
    };
    return <Component {...props} onMouseEnter={changeVariant} />;
  };
}

Writing Custom Code Components

Import Necessary Modules:

  • Import motion from framer-motion.

  • Import addPropertyControls and ControlType from framer.

Define the Component Function:

  • Use a function-based component.

  • Inline CSS styles.

Implement Property Controls:

  • Use addPropertyControls to add customizable properties in the Framer UI.


Property Controls

Property controls allow users to configure the components props in Framer's UI. Below are the different types of property controls and their usage:

Adding Property Controls:

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return <div>{props.text}</div>;
}
MyComponent.defaultProps = {
  text: "Hello World!",
};
addPropertyControls(MyComponent, {
  text: { type: ControlType.String, title: "Text" },
});

Types of Property Controls

String

Accepts plain text values, is provided directly as a property.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return <div>{props.text}</div>;
}
MyComponent.defaultProps = {
  text: "Hello World!",
};
addPropertyControls(MyComponent, {
  text: {
    type: ControlType.String,
    defaultValue: "Hello World",
    placeholder: "Type something…",
  },
});

Number

Accepts any numeric value, is provided directly as a property. Can be displayed as a range slider or a stepper.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
    <motion.div rotateZ={props.rotation} style={{ width: 50, height: 50 }}>
      {props.rotation}
    </motion.div>
  );
}
MyComponent.defaultProps = {
  rotation: 0,
};
addPropertyControls(MyComponent, {
  rotation: {
    type: ControlType.Number,
    defaultValue: 0,
    min: 0,
    max: 360,
    unit: "deg",
    step: 0.1,
    displayStepper: true,
  },
});
Boolean
A control that displays an on/off toggle.
import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
    <div style={{ minHeight: 50, minWidth: 50 }}>
      {props.showText ? "Hello World" : null}
    </div>
  );
}
MyComponent.defaultProps = {
  showText: true,
};
addPropertyControls(MyComponent, {
  showText: {
    type: ControlType.Boolean,
    title: "Show Text",
    defaultValue: true,
    enabledTitle: "On",
    disabledTitle: "Off",
  },
});

Color

A color value included in the component props as a string.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return <div style={{ backgroundColor: props.background, width: 50, height: 50 }} />;
}
MyComponent.defaultProps = {
  background: "#fff",
};
addPropertyControls(MyComponent, {
  background: {
    type: ControlType.Color,
    defaultValue: "#fff",
  },
});

Enum

A list of options. Contains primitive values where each value is unique.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  const value = props.value || "a";
  const colors = { a: "red", b: "green", c: "blue" };
  return (
    <div
      style={{
        backgroundColor: colors[value],
        width: 50,
        height: 50
      }}
    >
      {value}
    </div>
  );
}
MyComponent.defaultProps = {
  value: "a",
};
addPropertyControls(MyComponent, {
  value: {
    type: ControlType.Enum,
    defaultValue: "a",
    displaySegmentedControl: true,
    options: ["a", "b", "c"],
    optionTitles: ["Option A", "Option B", "Option C"],
  },
});

Image

An image included in the component props as a URL string.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return <img src={props.image} style={{ width: 200, height: 200 }} />;
}
MyComponent.defaultProps = {
  image: "",
};
addPropertyControls(MyComponent, {
  image: {
    type: ControlType.Image,
  },
});

File

Allows the user to pick a file. Included in component props as a URL string.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
      <video
        style={{ objectFit: "contain", ...props.style }}
        src={props.filepath}
        controls
      />
  );
}
MyComponent.defaultProps = {
  filepath: "",
};
addPropertyControls(MyComponent, {
  filepath: {
    type: ControlType.File,
    allowedFileTypes: ["mov"],
  },
});

Array

Allows multiple values per control type, provided as an array.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  const frames = props.images.map(image => {
    return <img src={image} style={{ width: 50, height: 50 }} />;
  });
 
  return <div style={{ display: "flex", gap: 10 }}>{frames}</div>;
}
MyComponent.defaultProps = {
  images: [],
};
addPropertyControls(MyComponent, {
  images: {
    type: ControlType.Array,
    control: {
      type: ControlType.Image
    },
    maxCount: 5,
  },
});

Object

Allows for grouping multiple properties as an object.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
    <div
      style={{
        opacity: props.myObject.opacity,
        backgroundColor: props.myObject.tint
      }}
    />
  );
}
MyComponent.defaultProps = {
  myObject: {
    opacity: 1,
    tint: "#09F"
  }
};
addPropertyControls(MyComponent, {
  myObject: {
    type: ControlType.Object,
    controls: {
      opacity: { type: ControlType.Number },
      tint: { type: ControlType.Color },
    }
  },
});

Transition

Allows for editing Framer Motion transition options within the Framer UI.

import { motion } from "framer-motion";
import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
      <motion.div
         animate={{ scale: 2 }}
         transition={props.transition}
      />
  );
}
MyComponent.defaultProps = {
  transition: { duration: 0.5 }
};
addPropertyControls(MyComponent, {
  transition: {
      type: ControlType.Transition,
  },
});

Font

Provides extended font controls, allowing customization of font family, size, weight, line height, and spacing.

import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 */
export function MyComponent(props) {
  return (
    <div style={{ fontFamily: props.font.family, fontSize: props.font.size }}>
      {props.text}
    </div>
  );
}
MyComponent.defaultProps = {
  text: "Hello World!",
  font: {
    family: "Inter",
    size: 16,
    weight: 400,
    lineHeight: "1.5em",
  }
};
addPropertyControls(MyComponent, {
  text: { type: ControlType.String, title: "Text" },
  font: {
    type: ControlType.Font,
    title: "Font",
    defaultValue: "Inter",
    controls: "extended",
  },
});

Applying Extended Font Control

To apply extended font control properties to a component, use the “...font” approach, instead of applying props.fontFamily, props.fontSize and everything separately. This allows you to easily spread the font properties onto the component's style, enabling full customization of the font settings directly within Framer.


IMPORTANT: If you try applying font properties separately like props.fontFamily, props.fontSize and so on, it will not work. Always use “...font”.


Example Component with Extended Font Control:

import * as React from "react";
import { addPropertyControls, ControlType } from "framer";
import { motion } from "framer-motion";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 300
 * @framerIntrinsicHeight 50
 * @framerSupportedLayoutWidth any
 * @framerSupportedLayoutHeight any
 */
export default function TextWithFontComponent(props) {
  const {
    text,
    font,
    textColor,
    backgroundColor,
    padding,
    borderRadius,
  } = props;
  const style = {
    backgroundColor,
    color: textColor,
    padding: `${padding}px`,
    borderRadius: `${borderRadius}px`,
    ...font,
  };
  return <motion.div style={style}>{text}</motion.div>;
}
TextWithFontComponent.defaultProps = {
  text: "Sample Text",
  font: {
    fontFamily: "Inter",
    fontWeight: "Regular",
    fontSize: 16,
    lineHeight: "1.5em",
  },
  textColor: "#000",
  backgroundColor: "#fff",
  padding: 10,
  borderRadius: 5,
};
addPropertyControls(TextWithFontComponent, {
  text: { type: ControlType.String, title: "Text" },
  font: {
    type: ControlType.Font,
    title: "Font",
    defaultValue: TextWithFontComponent.defaultProps.font,
    controls: "extended",
  },
  textColor: {
    type: ControlType.Color,
    title: "Text Color",
    defaultValue: "#000",
  },
  backgroundColor: {
    type: ControlType.Color,
    title: "Background Color",
    defaultValue: "#fff",
  },
  padding: {
    type: ControlType.Number,
    title: "Padding",
    defaultValue: 10,
    min: 0,
    max: 100,
    step: 1,
  },
  borderRadius: {
    type: ControlType.Number,
    title: "Border Radius",
    defaultValue: 5,
    min: 0,
    max: 50,
    step: 1,
  },
});

Framer Specific Declarations

Framer-specific annotations provide additional metadata about the components. These annotations control component sizing and default behavior.

Common Annotations:

  • @framerDisableUnlink: Prevents the component from being unlinked from the main component when modified.

  • @framerIntrinsicWidth: Sets the intrinsic width of the component.

  • @framerIntrinsicHeight: Sets the intrinsic height of the component.

  • @framerSupportedLayoutWidth: Controls the supported width options (any, auto, fixed).

  • @framerSupportedLayoutHeight: Controls the supported height options (any, auto, fixed).

Example with Annotations:

import { motion } from "framer-motion";
import { addPropertyControls, ControlType } from "framer";
/**
 * @framerDisableUnlink
 * @framerIntrinsicWidth 100
 * @framerIntrinsicHeight 100
 * @framerSupportedLayoutWidth any
 * @framerSupportedLayoutHeight auto
 */
export function MyComponent(props) {
  return (
    <motion.div style={{ ...props.style, backgroundColor: props.color }}>
      {props.text}
    </motion.div>
  );
}
MyComponent.defaultProps = {
  text: "Hello World!",
  color: "#09f",
};
addPropertyControls(MyComponent, {
  text: { type: ControlType.String, title: "Text" },
  color: { type: ControlType.Color, title: "Background Color" },
  font: {
    type: ControlType.Font,
    title: "Font",
    defaultValue: "Inter",
    controls: "extended",
  },
});

Conclusion

By using Code Overrides and Code Components, you can extend the functionality of your Framer projects significantly. Overrides allow for easy enhancements and interactions with existing elements, while components provide a robust way to introduce custom, complex features. Remember to always follow best practices for coding, such as spreading props correctly and leveraging Framer’s built-in motion features for animations. The use of property controls ensures that your components are customizable and versatile.

Your Task

I'm going to give you descriptions of code overrides or code components I want you to create. Keep your replies short and concise, and focus mostly on producing correct code according to the documentation above.


Reply with "What should we create?" if we can start.

PRIVACY POLICY

ALL RIGHTS RESERVED © (2024)

Local Times

ON, Canada

9:26 AM

NY, Unites States of America

9:26 AM

KA, India

7:56 PM

MH, India

7:56 PM