Code hacks

by

Anoop

Guard logic for Canvas vs Preview

Guard

When a code component runs inside Framer it can be in two places: the Canvas (editor) or Preview (runtime). Some APIs and behaviors are unsafe or slow in the Canvas. Always detect the environment and guard side effects accordingly.

Why

Canvas runs inside the editor. It can be slow, may not have

When you build code components for Framer, you're writing React but your components live in two worlds:

the Canvas (where designers drop, resize, and preview in the editor)

and the Preview (the actual runtime your end users see).

Most of the time, we don't think about the difference... until something breaks.

Maybe your fetch() runs 50 times in the editor.

Maybe window is undefined and your component silently fails.

Or worse: your component lags the entire Canvas because it's trying to animate or load resources in the background.

This is where one simple guard can save you hours of debugging.

The Golden Rule

Always check whether your component is running inside the Canvas or Preview before running logic that touches the browser, network, or performance-heavy code.

Framer gives us a built-in way to detect that.

import { RenderTarget } from "framer"

const isCanvas = RenderTarget.current() === RenderTarget.canvas

This single line changes everything.

Why It Matters

The Canvas is not a browser: it's a controlled environment where Framer renders your component tree inside the editor.

Some APIs are restricted or behave differently.

Here's what typically breaks when you don't guard:

  • fetch() loops indefinitely because the Canvas re-renders often

  • ResizeObserver or IntersectionObserver runs thousands of times while dragging

  • requestAnimationFrame causes the editor to lag

  • Fonts, videos, or scripts try to load before the preview even starts

  • You trigger side effects that Framer can't clean up when switching pages

By guarding your logic, you give the editor a lightweight placeholder and reserve the heavy lifting for preview time.

The Core Pattern

Here's a basic skeleton you can copy into any Framer code component.

import * as React from "react"
import { RenderTarget } from "framer"

type Props = {
  title?: string
}

export default function GuardedWidget({ title = "Guarded Component" }: Props) {
  const isCanvas = RenderTarget.current() === RenderTarget.canvas
  const [data, setData] = React.useState<string |="" null="">(null)

  React.useEffect(() => {
    if (isCanvas) return

    let mounted = true

    async function load() {
      await new Promise((r) => setTimeout(r, 600))
      if (!mounted) return
      setData("Loaded content from API")
    }

    load()

    return () => {
      mounted = false
    }
  }, [isCanvas])

  if (isCanvas) {
    return (
      <div style="{{" minheight:="" 100,="" padding:="" 16,="" borderradius:="" 8,="" background:="" "linear-gradient(180deg,="" rgba(0,0,0,0.02),="" transparent)",="" }}="">
        <div style="{{" fontsize:="" 14,="" opacity:="" 0.85="" }}="">Canvas Placeholder</div>
        <div style="{{" fontsize:="" 12,="" color:="" "rgba(0,0,0,0.45)"="" }}="">
          Logic disabled in Canvas
        </div>
      </div>
    )
  }

  return (
    <div style="{{" minheight:="" 100,="" padding:="" 16,="" borderradius:="" 8,="" background:="" "#fff",="" boxshadow:="" "0="" 4px="" 12px="" rgba(0,0,0,0.08)",="" }}="">
      <div style="{{" fontsize:="" 16,="" fontweight:="" 500="" }}="">{title}</div>
      <div style="{{" fontsize:="" 13,="" color:="" "rgba(0,0,0,0.6)"="" }}="">
        {data ?? "Loading..."}
      </div>
    </div>
  )
}
<

What's happening here

  1. We detect the environment once at the top

  2. Inside useEffect, we skip all side effects if we're on the Canvas

  3. We render a minimal placeholder instead of the full component in edit mode

  4. In Preview, the real logic runs: here simulated with a mock async call

This makes the component instant in the editor, but fully functional at runtime.

Common Use Cases

Use case

What to guard

Fetching data

Skip all API calls in Canvas. Instead, render mock data or a placeholder.

Playing media

Avoid autoplay or preloading in Canvas. Use static preview thumbnails instead.

Running timers or loops

Only startsetIntervalorrequestAnimationFramein Preview.

Loading fonts or scripts

Guarddocument.fonts.load()or any DOM injection logic.

Using observers

DisconnectResizeObserverorIntersectionObserveron unmount. Don't start them in Canvas.

Bonus: Mock Data Mode

Sometimes you do want to show something in Canvas that feels real but without hitting your live API.

Here's how to handle that safely.

const mockData = [
  { title: "Design with confidence" },
  { title: "Preview with purpose" },
  { title: "Guard everything" },
]

const displayData = isCanvas ? mockData : fetchedData

This lets you keep the Canvas visual while still protecting performance.

Real-World Example

Here's a snippet from one of our internal components: a live analytics widget that fetches daily revenue.

If you remove the guard, it will spam the API every few seconds while designing.

React.useEffect(() => {
  if (RenderTarget.current() === RenderTarget.canvas) return

  const controller = new AbortController()
  async function getRevenue() {
    const res = await fetch("/api/revenue", { signal: controller.signal })
    const json = await res.json()
    setRevenue(json.total)
  }

  getRevenue()

  return () => controller.abort()
}, [])

Best Practices Checklist

  • Detect environment once at the top

  • Skip side effects in Canvas

  • Show a clean placeholder in Canvas mode

  • Clean up observers, timers, and listeners on unmount

  • Avoid DOM-heavy logic in the editor

  • Never fetch live data or run loops inside Canvas

  • Make defaults beautiful so the placeholder still feels designed

Closing Thought

The difference between a "Framer beginner component" and a "production-ready component" often comes down to this guard.

Checking the environment before you run logic isn't just about avoiding bugs: it's about designing for Framer itself.

Your users shouldn't feel lag while editing. Your previews shouldn't behave differently than design mode.

So the next time you add useEffect, fetch, or a heavy animation:

just ask yourself:

"Should this run in the Canvas?"

If the answer is no, you already know what to do.


Code components

Beautiful video player

Free

Beautiful video player

Free

Beautiful video player

Free

HLS video player

$19

HLS video player

$19

HLS video player

$19

Dot Matrix - SVG

Free

Dot Matrix - SVG

Free

Dot Matrix - SVG

Free

FAQs accordion

Free

FAQs accordion

Free

FAQs accordion

Free

Launch count down timer

Free

Launch count down timer

Free

Launch count down timer

Free

View port width

Free

View port width

Free

View port width

Free

Theme switcher

Free

Theme switcher

Free

Theme switcher

Free