better way to embed figma prototypes
i'm a huge fan of figma prototypes. they have proven to be a great way to share interactions (rather than static designs) with peers and clients. developers and clients often have trouble inferring interaction and animation from static designs since they don't have declarative knowledge of UI design patterns. figma prototypes are a great way to bridge that gap with minimal effort.
below is a prototype of a spreadsheet modal. try clicking the "Download" button.
figma's "smart animate" mode is particularly useful for prototyping animations. it essentially generates a tween between two artboards based on the position and size of the layers in each frame. Sometimes results are surprisingly good. while it tends to perform pretty poorly on complex animations, it's great for simple interactions that do add polish to a design.
embedding figma prototypes
figma prototypes can be shared via a link, but it's also possible to embed them in a webpage. it's awesome for quick demos within a website or blog post without writing a lot of one-off code. MDX makes it even easier to embed figma prototypes in a blog post.
the default figma embed is... not great. too busy, not responsive, feels like you're looking at someone else's workspace instead of experiencing the design.
this is how an embed from Figma looks by default:
<iframe style="border: none;" width="800" height="450" src="https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/proto/..."></iframe>
already, there are a few annoying things about this embed:
- the iframe is not responsive
- the figma embed UI is too busy over the prototype
prop API
if we're embedding a lot of prototypes, it's nice to have a reusable component that handles these issues. i wanted this component to:
- be responsive to the container width
- bring my own UI for metadata and controls
- link to the original Figma file
given the constraints, here's the api i came up with:
type FigmaEmbedProps = {
url: string;
aspectRatio: number;
title: string; // for accessibility
description: string; // for accessibility
};
i'll admit that having to provide an aspect ratio is pretty awkward, but it's the only way to make the embed responsive. The content is not only cross-origin but rendered on a canvas inside of figma's app code, so aspect ratio inference is not possible.
given the aspect ratio, we can calculate the height of the embed based on the width of the container.
as for getting a link to the figma file, we can parse the url
parameter to extract the file id and construct a link to the editor page.
function FigmaEmbed({ url, aspectRatio }: FigmaEmbedProps) {
if (!url.includes("hideUI")) {
// if the url is unopinionated about hiding
// the Figma UI, then hide it
url += "%26hide-ui%3D1";
}
return (
<div
style={{
position: "relative",
height: "auto",
aspectRatio,
}}
>
<iframe
style={{
width: "100%",
height: "100%",
border: "none",
}}
// forward provided embed url
src={url}
loading="lazy"
/>
</div>
);
}
import { FigmaLogo } from "lucide-react";
/**
* Extract the file id from a Figma prototype url
*/
function extractFileURL(url: string) {
const regex = /proto%2F(.*?)%2F/;
const match = url.match(regex);
const id = match?.[1];
return id;
}
function FigmaEmbed({ url, aspectRatio }: FigmaEmbedProps) {
const fileId = extractFileURL(url);
if (!url.includes("hideUI")) {
// if the url is unopinionated about
// hiding the Figma UI, then hide it
url += "%26hide-ui%3D1";
}
return (
<div className="figma-embed">
{fileId && (
<a href={`https://www.figma.com/file/${fileId}`} target="_blank">
<FigmaLogo />
Open in Figma
</a>
)}
<iframe src={url} loading="lazy" />
</div>
);
}
designing overlays
i wanted to create a floating link to the figma file and have it positioned over the prototype itself. this link needs to always be readable, and we don't know what the background of the embed's artboard will be until runtime.
there are a few solutions to this problem, but I decided to use CSS mix-blend-mode and background-blend-mode to maintain contrast. most of the time the link will be over a pure color, so difference
and darken
work well to provide basic readability and a background for a hover state.
.figma-embed {
position: relative;
/* ... */
a {
position: absolute;
top: 16px;
left: 16px;
z-index: 1;
mix-blend-mode: difference;
background-blend-mode: darken;
color: white;
background: transparent;
&:hover {
background: #121313;
}
}
}
putting it all together
import FigmaEmbed from '@/components/mdx/blog/figma-embed.tsx'
<FigmaEmbed
url="https://embed.figma.com/proto/vCuTTtlfkq7UM7qnB37Dtb/Embedded-Figma-Protos?node-id=97-574&p=f&scaling=min-zoom&content-scaling=fixed&page-id=95%3A549&embed-host=share"
aspectRatio={16/9}
title="CheckBox Prototype"
description="Try clicking the CheckBox to see the interaction."
/>
here's the final React component (code above) made to fit with the rest of this site:
once you strip away figma's ui and make it responsive, something clicks. it becomes about the design, not the tool.
-
there is also a productivity boost, even without this context loss. i'm usually developing my own designs, but even with framer motion, I just find it easier to iterate on flows and interactions with prototyping animations in Figma. ↩
-
a tween is a type of animation that interpolates between two states. for example, a tween between two artboards might move a layer from one position to another. read more on wiki. ↩