Archive » Code Components (pre X22)

Code Components (pre X22)

This page is from before the launch of Framer X22. Since X22 we can use Function Components (read about them here). The text below is about Class Components, which still work in Framer but are more complicated to write.

Design Components are static, they don’t change. Code Components, on the other hand, can be interactive.

In a Code Component can do anything that’s also possible in front-end web development. You can talk to APIs to get some information from a server, animate SVGs with the use of a library you imported, play sounds with another library, etc.

Let’s see how you make one.

When you create a new Code Component, Framer will open the newly created .tsx file in VS Code:

import * as React from "react";
import { PropertyControls, ControlType } from "framer";

// For the best editing experience in VSCode, install Prettier
// https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

// Everything below is standard React. If you are new, start here:
// https://reactjs.org/docs/getting-started.html#learn-react
// https://reactjs.org/docs/components-and-props.html

// We can tell TypeScript to help us by defining our types
// https://www.typescriptlang.org/docs/handbook/basic-types.html
type Props = { text: string };

export class My_first_component extends React.Component<Props> {
  // Return the component contents in JSX
  // https://reactjs.org/docs/introducing-jsx.html
  render() {
    return <div style={style}>{this.props.text}</div>;
  }

  // Set default values for props if there are none
  // https://reactjs.org/docs/react-component.html#defaultprops
  static defaultProps: Props = {
    text: "Hello World!"
  };

  // Add Framer UI for this component (in the properties panel)
  // https://framer.com/learn/docs/components#code
  static propertyControls: PropertyControls<Props> = {
    text: { type: ControlType.String, title: "Text" }
  };
}

// Define some standard CSS for your component
const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};
download this project

Without the comments it looks like this:

import * as React from "react";
import { PropertyControls, ControlType } from "framer";

type Props = { text: string };

export class My_first_component extends React.Component<Props> {
  render() {
    return <div style={style}>{this.props.text}</div>;
  }

  static defaultProps: Props = {
    text: "Hello World!"
  };

  static propertyControls: PropertyControls<Props> = {
    text: { type: ControlType.String, title: "Text" }
  };
}

const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};

We’ll rebuild this component step-by-step so that you’ll have an understanding of what every line of code actually does.

Importing React

The very first line imports the React library.

import * as React from "react";

By using *, we import all of it, and we’re putting it inside a variable called React.

So, for instance, React’s Component class (which we’ll extend to make our own components), is now inside React.Component.

In fact, let’s start with a really basic component, this one:

import * as React from "react";

export class My_first_component extends React.Component {
  render() {
    return <h1>Hi there.</h1>;
  }
}
download this project

And then we’ll add all the other stuff again, to eventually arrive back at Framer’s default component.

Extending the React.Component class

What is a React component? It’s a function that returns HTML.

In standard React you could just write the function like this:

function My_first_component(props) {
  return <h1>Hi there.</h1>;
}

This is a ‘Function component’ (also called ‘Functional component’, or ‘Stateless component’) and soon we’ll be able to use those in Framer, but for now, we use ‘Class Components.’

You create a class component by extending the existing React.Component class.

export class My_first_component extends React.Component {
  render() {
    return <h1>Hi there.</h1>;
  }
}

What’s a class? That’s an existing JavaScript object that already contains certain properties and functions. By extending it we’re saying “I’ll have one of those so that I can use what it already contains.” (while still being able to add or change things)

class My_first_component extends React.Component

In the above line, I’m creating a new class, named My_first_component, that extends the existing Component class inside React.

Exporting the new class

We add export so that it can be read (imported) by Framer and displayed in the Components Panel on the left.

export class My_first_component extends React.Component {
    …

Here the same is true as with Overrides: omit export, and your component will not show up inside Framer.

Rendering the content

There’s the one sole function any component should always contain: render().

  render() {
    return <h1>Hi there.</h1>;
  }

And inside this function you have to do one thing: return what should be shown, in other words, rendered, on the screen.

It’s not HTML, it’s JSX

What we’re returning looks like HTML (and can also contain any HTML tag), but is actually JSX, a syntax extension (hence the ‘X’) to JavaScript.

Returning plain HTML would look like this:

  render() {
    return "<h1>Hi there.</h1>";
  }
download this project

… and doesn’t really work, because React is expecting JSX.

JSX has the advantage that you can insert JavaScript code by placing it between { curly braces }.

You could, for instance, save a few text strings in variables:

const adjective = "young";
const role = "padawan";

… and then use them inside your return.

export class My_first_component extends React.Component {
  render() {
    return (
      <h1>
        Hello {adjective} {role}.
      </h1>
    );
  }
}

(At this point VS Code will start to put your return code on different lines, just because it’s getting a bit long.)

Note that you need to put ( brackets ) around the content when it’s on more than one line. (VS Code will add them automatically though.)

download this project

Any JavaScript expression can be used when encapsulated in curly braces, like for instance a calculation:

export class My_first_component extends React.Component {
  render() {
    return <h1>Six times seven is {6 * 7}</h1>;
  }
}
download this project

Sometimes you might see {{ double curly braces }} in JSX code.

That’s no special syntax, it’s just two things happening at the same time.

In JavaScript, you define an object with curly braces, like this one, bigAndRed, which contains two CSS properties:

const bigAndRed = { color: "red", fontSize: "40px" };

You could then use it like this:

export class My_first_component extends React.Component {
  render() {
    return <h1 style={bigAndRed}>Hi there.</h1>;
  }
}

But you can also write everything inline, you can directly insert the ‘object literal’:

export class My_first_component extends React.Component {
  render() {
    return <h1 style={{ color: "red", fontSize: "40px" }}>Hi there.</h1>;
  }
}
download this project

CSS styling

Talking about CSS, in Framer’s example component all the CSS properties are put in an object named style, at the end of the file:

// Define some standard CSS for your component
const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};

Note that the properties are all written in Camel Case.

It’s just an object, so why isn’t it written like this?

const style = {
  height: "100%",
  display: "flex",
  …

The React.CSSProperties part is a bit of TypeScript.

TypeScript type definition for the CSS style object

Remember what we said about TypeScript’s type annotations in the Overrides chapter? That you could use them to signal that a particular variable should, for instance, always contain a number.

let totalPrice: number = 0;

// updating the price
totalPrice = 100;

Well, React.CSSProperties is a special type annotation for React. It defines how a CSS style object should look so that your editor (VS Code) can check for typos.

And it provides auto-completion! Try to add a CSS property. When you just type, e.g. ‘font’ it will bring up all possible options.

By the way, the file extension we use for our Code Components (and also Overrides) is .tsx. This stands for JSX with TypeScript.

Moving on.

Our simple component is getting a bit more complicated. It now looks like this:

import * as React from "react";

export class My_first_component extends React.Component {
  render() {
    return <div style={style}>Hi there.</div>;
  }
}

const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};
download this project

I’m now using a <div> instead of the former <h1>, and I applied the CSS properties contained in style to it.

Now the component has the same styling as Framer’s example.

Adding Custom Properties

Our component isn’t interactive or customizable, we might as well have created a Design Component.

Let’s make it customizable by adding custom properties. We’ll start simple, with just one text property.

TypeScript property definition

First some more TypeScript. We define a type for the custom properties (Props) that we want our component to have. Like this:

type Props = { text: string };

It only contains one property, text, and we say that text should always contain a string value.

We pass that information to our class by adding it like this: <Props>:

export class My_first_component extends React.Component<Props> {
  …

Now VS Code will make sure that the text property always contains a string (and will warn you when you try to give it another kind of value).

Default property values

Our property should have a default value, the text string that it contains when you drag the component to the canvas.

Add this inside the My_first_component class:

  static defaultProps: Props = {
    text: "Hello World!"
  };

We’re saying that these default values (again, currently just one) should conform to the Props we defined higher up. By naming the object defaultProps, Framer will find them and use them as initial values.

The static keyword is something you just have to add. (It denotes that this object can only be used by the class itself, and not by instances of the class.)

Now that we have a text property, we can use it in the JSX:

  render() {
    return <div style={style}>{this.props.text}</div>;
  }

The this keyword refers to the component class itself, and its properties live inside props.

Where does this props object come from? It’s part of the original React.Component class, the one we extended.

As a reminder: we don’t use this when we’re including the style object in the <div>, because it’s not part of our class. (It’s not defined inside the export class My_first_component ….)

You should now have this:

import * as React from "react";

type Props = { text: string };

export class My_first_component extends React.Component<Props> {
  render() {
    return <div style={style}>{this.props.text}</div>;
  }
  
  static defaultProps: Props = {
    text: "Hello World!"
  };
}

const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};
download this project

Still, there’s no real advantage compared to a Design Component. We might have a custom text property, but we can’t even change its text.

Let’s add a Property Control so that we can change the text from within the Framer UI.

Property Controls

Currently, our component shows this in the Properties Panel (when you select the component on the Canvas):

And we would like to have the option to change the text directly in the Properties Panel, like this:

Property Controls are, of course, not a part of standard React. They are added by Framer, so we need to import them from the Framer Library.

import { PropertyControls, ControlType } from "framer";

The first one that we import, PropertyControls is a TypeScript type that describes how a set of property controls should look, and ControlType contains the details of all possible kinds of controls.

The static object you add to the class should be named propertyControls and be of this PropertyControls type.

  static propertyControls: PropertyControls<Props> = {
    
  };

(Note that we’re also passing in our <Props> type definitions.)

For every property (again, just one, for now: text) you give it an object with the type of the property and the title we want to see in the properties panel.

    text: { type: ControlType.String, title: "Text" }

In this case, we want a text input field in the Framer interface, so we use ControlType.String, and we want the label next to it to read "Text".

And that’s all! You should now have a code component that’s identical to Framer’s example, and also understand what every line of code does.

import * as React from "react";
import { PropertyControls, ControlType } from "framer";

type Props = { text: string };

export class My_first_component extends React.Component<Props> {
  render() {
    return <div style={style}>{this.props.text}</div>;
  }

  static defaultProps: Props = {
    text: "Hello World!"
  };

  static propertyControls: PropertyControls<Props> = {
    text: { type: ControlType.String, title: "Text" }
  };
}

const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};
download this project

By the way, the code in our component has the same order as Framer’s. But you can rearrange stuff.

You can, for instance, place the style object at the beginning (but always after the imports, obviously).

And the order of defaultProps, propertyControls, and render() inside the component also isn’t relevant. You might often see the render() function placed at the end.

import * as React from "react";
import { PropertyControls, ControlType } from "framer";

const style: React.CSSProperties = {
  height: "100%",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  textAlign: "center",
  color: "#8855FF",
  background: "rgba(136, 85, 255, 0.1)",
  overflow: "hidden"
};

type Props = { text: string };

export class My_first_component extends React.Component<Props> {
  static defaultProps: Props = {
    text: "Hello World!"
  };

  static propertyControls: PropertyControls<Props> = {
    text: { type: ControlType.String, title: "Text" }
  };

  render() {
    return <div style={style}>{this.props.text}</div>;
  }
}

3 comments on “Code Components (pre X22)”

Comments are closed.