Services About Us Why Choose Us Our Team Development Workflow Technology Stack Case Studies Portfolio Blog Free Guides Shopify Audit ($499) Estimate Project Contact Us
← Back to Blog

Framer Custom Code Components: The Production Patterns That Actually Work

Framer's low-code canvas gets you 80% of the way. The other 20% — animated counters, third-party library embeds, form logic, custom scroll behavior — needs code components and overrides. Here are the patterns we use on production Framer sites, including the March 2026 CMS change that broke half the internet's examples.

TV
TechVinta Team June 28, 2026 Full-stack development agency specializing in Rails, React, Shopify & Sharetribe
Framer Custom Code Components: The Production Patterns That Actually Work

The short answer: when do you need Framer code components?

Use a code component when you need a fully custom interactive UI element — an animated chart, a third-party map, a video player with custom controls, a form with validation logic — that Framer's canvas tools can't build natively. Use a code override when you want to add behavior (scroll-triggered animation, tracking events, conditional class toggling) to an existing canvas layer without replacing it entirely.

Watch first: how Framer property controls work

Before the patterns below, watch this 10-minute walkthrough from Framer educator Seth. It shows the addPropertyControls flow end-to-end — the part most tutorials rush past — which determines whether non-developers on your team can actually use the component without touching code.

Components vs. overrides: pick the right tool first

Question Code Component Code Override
Renders on canvas? Yes No — modifies an existing layer
Needs its own design? Yes — owns all markup and styles No — wraps a layer the designer built
Supports addPropertyControls? Yes No
Typical use case Custom chart, embedded map, form widget Scroll animation, analytics event, toggle behavior
Written as Standard React component (TypeScript) React HOC with forwardRef

Pattern 1: the minimal production-ready code component

Framer requires three things for a code component to behave reliably on the canvas and in production: the correct TypeScript structure, sizing annotations, and property controls. Skip any one of them and you get either a broken canvas preview or a component non-developers can't configure.

import { addPropertyControls, ControlType } from "framer"

// @framerSupportedLayoutWidth any
// @framerSupportedLayoutHeight any
// @framerIntrinsicWidth 320
// @framerIntrinsicHeight 160

interface Props {
  label: string
  color: string
  onClick?: () => void
}

export default function StatBadge({ label, color, onClick }: Props) {
  return (
    <div
      onClick={onClick}
      style={{
        background: color,
        borderRadius: 8,
        padding: "12px 24px",
        cursor: "pointer",
        fontFamily: "sans-serif",
        fontWeight: 600,
        color: "#fff",
      }}
    >
      {label}
    </div>
  )
}

addPropertyControls(StatBadge, {
  label: { type: ControlType.String, defaultValue: "Click me" },
  color: { type: ControlType.Color, defaultValue: "#3b82f6" },
})

Two things to never skip: the @framer annotation comments at the top, and the addPropertyControls call at the bottom. Without the annotations, the component sizes arbitrarily on the canvas. Without property controls, designers can only configure the component by editing your TypeScript, which breaks the design-dev handoff.

Also: no generics. Framer's TypeScript runtime does its own type handling, and generic types (`Component`) break the compiler. Use specific interfaces.

Pattern 2: code overrides with forwardRef

Code overrides are HOCs — they wrap a canvas layer without replacing its visual design. The naming convention is withFeatureName. The only required structural element is forwardRef, which is not optional: without it, Framer's effects and link bindings stop working on the overridden layer.

import { ComponentType, forwardRef, useState } from "react"

export function withScrollCounter(Component: ComponentType): ComponentType {
  return forwardRef((props: any, ref) => {
    const [scrolled, setScrolled] = useState(false)

    const handleScroll = () => {
      if (window.scrollY > 100 && !scrolled) {
        setScrolled(true)
        // fire your analytics event here
      }
    }

    if (typeof window !== "undefined") {
      window.addEventListener("scroll", handleScroll, { passive: true, once: true })
    }

    return <Component {...props} ref={ref} data-scrolled={scrolled} />
  }) as ComponentType
}

The Framer overrides documentation covers the HOC API, but the forwardRef requirement is buried. We've seen this break link bindings on three separate client sites where it was omitted.

Pattern 3: third-party library embedding

Embedding a chart library (Recharts, Chart.js, Victory), a map (Mapbox, Leaflet), or a rich-text editor inside a Framer canvas is the most common "we need a code component" moment on agency projects. The pattern is the same for any library: import it, wrap it in a component, expose the data source via property controls.

import { addPropertyControls, ControlType } from "framer"
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"

// @framerSupportedLayoutWidth any-prefer-fixed
// @framerSupportedLayoutHeight any-prefer-fixed
// @framerIntrinsicWidth 400
// @framerIntrinsicHeight 300

const defaultData = [
  { name: "Jan", value: 40 },
  { name: "Feb", value: 65 },
  { name: "Mar", value: 52 },
]

export default function SalesChart({ barColor, jsonData }) {
  const data = (() => {
    try { return JSON.parse(jsonData) } catch { return defaultData }
  })()

  return (
    <ResponsiveContainer width="100%" height="100%">
      <BarChart data={data}>
        <XAxis dataKey="name" />
        <YAxis />
        <Tooltip />
        <Bar dataKey="value" fill={barColor} />
      </BarChart>
    </ResponsiveContainer>
  )
}

addPropertyControls(SalesChart, {
  barColor: { type: ControlType.Color, defaultValue: "#6366f1" },
  jsonData: {
    type: ControlType.String,
    defaultValue: JSON.stringify(defaultData),
    displayTextArea: true,
  },
})

One practical note: Framer runs inside a sandboxed iframe on the canvas. Libraries that assume a full browser window (some mapping libraries, WebGL renderers) often need an SSR guard — wrap any window or document access in typeof window !== "undefined" checks or the canvas preview will throw before the user even drags the component in.

The March 2026 CMS change: what broke and how to fix it

As of March 2026, Framer removed the ability to access CMS collections from inside code components and overrides. Specifically: querying props.children.props.query.from.data, importing collection modules directly, or using any programmatic CMS access from inside a code file no longer works.

Framer's stated reason: performance, stability, and enabling larger collection support. If you find a tutorial that uses any of those patterns, it predates this change.

The current workaround from Framer is to use native CMS bindings from the canvas — bind CMS fields to a component's property controls via the Framer UI, not from code. Data flows canvas-in, not code-out. This is actually the cleaner pattern for design-dev separation; the breaking change forced teams away from an antipattern.

If you have existing code components that need CMS data, the migration path is: expose a string or JSON property control on the component, then bind that control to a CMS field in the Framer UI. Your code component becomes data-agnostic; Framer handles the CMS plumbing.

Sizing gotchas on the canvas

The most common support question on Framer builds we take over: "the component looks right in preview but wrong on the canvas." Almost always a sizing annotation issue.

  • @framerIntrinsicWidth and @framerIntrinsicHeight set the default size when the component is first dragged onto the canvas. Without them, Framer guesses — and guesses wrong for anything using ResponsiveContainer or percentage-based sizing.
  • @framerSupportedLayoutWidth any-prefer-fixed tells Framer to prefer a fixed width but allow the designer to stretch it. Use any if the component handles both fixed and fluid widths correctly.
  • Never use width: 100% on the root element if you set @framerIntrinsicWidth to a fixed value — they contradict each other and the canvas layout engine picks the wrong one depending on the parent container.

Where this fits in a real Framer production workflow

Code components are the extension point, not the default. On the agency projects we've shipped on Framer, the typical breakdown is: 75% Framer native components, 15% code overrides for behavioral layer, 10% full code components for things Framer's canvas genuinely can't do. If you're writing a code component for something Framer's primitives can handle, you're fighting the tool.

For teams deciding whether Framer is the right tool at all — whether a marketing site belongs on Framer or in a custom Next.js codebase — our Framer vs Webflow 2026 comparison covers the tradeoffs. The short version: Framer wins for design-first teams that want React under the hood. If your team is dev-first and the site is a secondary concern, Next.js is often the better call.

On the technical architecture side, if your Framer site needs a custom backend — form processing, server-side data, user authentication — that's where a Rails or Next.js API layer comes in. We covered the frontend architecture tradeoffs in our modern frontend architecture guide.

For a production example of where Framer code components solved a real business problem: the ZenHQ case study involved a custom filtering UI embedded inside a Framer marketing page — code component for the filter logic, code overrides for the scroll-triggered reveal animations, native Framer for everything else.

FAQ: Framer code components

What language do Framer code components use?
TypeScript and React. Framer's code editor runs TypeScript directly — no build step, no bundler to configure. Standard React 18 syntax works, including hooks, forwardRef, and context. Avoid generics; Framer's own type system handles type checking and generics break the compiler.

Can Framer code components access CMS data?
As of March 2026, no. Programmatic CMS access from inside code files was removed. Bind CMS fields to component property controls via the Framer canvas UI instead — the component receives data as props, Framer handles the CMS query. It's the correct separation of concerns anyway.

What's the difference between a code component and a code override?
Code components are standalone React components that render their own markup on the canvas. Code overrides are HOCs that wrap existing canvas layers to add behavior without changing the design. Components support addPropertyControls; overrides do not. Both require the same TypeScript/React skill set.

Why does my code component look different on canvas vs published?
Usually a sizing annotation issue. Add @framerIntrinsicWidth and @framerIntrinsicHeight to match your component's intended default dimensions. Also check for window/document access without SSR guards — Framer's canvas runs in a sandboxed iframe that can throw on browser globals before the full window environment is ready.

Can I use npm packages inside Framer code components?
Yes — Framer supports most npm packages via its built-in package manager. You import them the same way you would in any React project. Some packages that require native browser APIs (WebGL, Web Audio, certain mapping libraries) need guards for the canvas iframe environment.

How we can help

At TechVinta, our Framer build team ships production sites with custom code components weekly — from animated data visualizations to CMS-driven filtering UIs to third-party API embeds. We also handle the code-override layer for analytics, scroll behavior, and conditional interactions.

Need a Framer site with custom code components done right? Get a free estimate — we'll review your requirements and propose a plan within 48 hours.

Share this article:
TV

Written by TechVinta Team

We are a full-stack development agency specializing in Ruby on Rails, React.js, Vue.js, Flutter, Shopify, and Sharetribe. We write about web development, DevOps, and building scalable applications.

Keep Reading

TechVinta Assistant

Online - Ready to help

Hi there!

Need help with your project? We're online and ready to assist.

🍪

We use cookies for analytics to improve your experience. See our Cookie Policy.