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";
export function withLowerOpacity(Component): ComponentType {
return (props) => {
return <Component {...props} opacity={0.5} />;
};
}
Changing CSS Property with Override:
import type { ComponentType } from "react";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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";
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.