A Guide to Auto Layout in Figma for Developers Who Inherit Its Output

Audit the CSS that Figma auto layout actually generates: where it produces clean flexbox, where it falls back to absolute positioning, and what a developer must rewrite.

Most developers assume Figma auto layout exports clean, production-ready CSS. That assumption is wrong. It is a starting point, not a deliverable. Auto layout is a visual editor for flexbox. The code it generates is accurate for one-axis layouts. But it cannot express CSS Grid for two-axis layouts, and its absolute positioning fallback for constraints produces code you must rewrite. This guide maps what Figma emits to the CSS you actually write, shows where the conversion breaks, and gives you the replacement patterns.

Auto Layout to CSS Flexbox Conversion Accuracy

When you set a frame to Auto Layout in Figma, it generates CSS that maps directly to flexbox. The layoutMode value becomes flex-direction, itemSpacing becomes gap, and the alignment values become justify-content and align-items. The mapping is linear. What ships out of Figma Dev Mode is a faithful flexbox representation of your canvas.

Reading the Basic Export

Here is the CSS output from a simple auto layout frame with horizontal direction, a 16px gap, and 24px padding:

.container {
  display: flex;
  flex-direction: row;
  gap: 16px;
  padding: 24px;
  justify-content: flex-start;
  align-items: center;
}

That is exactly what you would write by hand. The conversion accuracy for single-axis layouts is high because the underlying model is identical. Figma’s primaryAxisAlignItems maps to justify-content, and counterAxisAlignItems maps to align-items. The generated code uses flex-start and flex-end rather than the start and end logical keywords. That is a minor stylistic difference, not a functional one.

The gap value in flexbox is the same gap in CSS. Figma’s itemSpacing applies between all children. That is precisely what the CSS gap does for flex containers. There is no margin hack, no > * + * selector, no negative margins. The box model is respected: padding on the frame, gap between items, and each child keeps its own dimensions.

When Space-Between Breaks

Where the conversion starts to slip is with Space between distribution. Figma’s SPACE_BETWEEN maps to justify-content: space-between, but only if the frame has a fixed width. If the frame is set to AUTO sizing on the primary axis, the items collapse to their content width and the spacing disappears. Set a fixed width on the frame. Without it, the generated CSS cannot produce the visual you designed. This is not a bug in the export. It is a mismatch between Figma’s mental model and actual CSS behaviour.

Figma Auto Layout Constraints vs CSS Grid

The most common failure is a two-axis layout. Figma auto layout has one direction: horizontal or vertical. You can nest frames to simulate a grid, but the generated CSS is nested flexbox, not CSS Grid. When you see the visual, rewrite it as display: grid. The export cannot express row tracks and column tracks simultaneously.

The Dashboard Problem

A dashboard with a header, a sidebar, and a main content area needs to align across both axes. In Figma, you nest auto layout frames: an outer vertical frame, an inner horizontal frame for the body. The export gives you this:

.dashboard {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.body {
  display: flex;
  flex-direction: row;
  gap: 16px;
}

.sidebar {
  flex: 0 0 240px;
}

.main {
  flex: 1 1 auto;
}

That works, but it has a structural weakness. The sidebar and the main content sit in the same flex row. They share a baseline but not a row track. If you need the sidebar to span two visual rows while the main content occupies one, flexbox cannot do that without wrapping hacks. CSS Grid can.

.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas:
    "header header"
    "sidebar main";
  gap: 16px;
}

.header {
  grid-area: header;
}

.sidebar {
  grid-area: sidebar;
}

.main {
  grid-area: main;
}

The grid version is fewer lines, clearer intent, and handles the two-axis alignment natively. Figma auto layout cannot generate this. The layoutMode value only supports HORIZONTAL, VERTICAL, or NONE. The export tool literally cannot represent a grid track. Rewrite this by hand. The constraint is not a Figma limitation; it is a fundamental difference between a one-axis and a two-axis layout system. Flexbox distributes items along a line. Grid places them in a plane.

Figma Dev Mode Auto Layout Code Snippet Audit

Figma Dev Mode shows the CSS for a selected node. It does not show the full context. A code snippet audit means checking three things: the display value, the position value, and the gap value. If the snippet says position: absolute, something is wrong. If the snippet omits gap and uses margins instead, the auto layout is not doing what you think.

The Four-Point Checklist

Run this audit on every snippet before you paste it into your codebase:

  1. Display: Does it say flex? If yes, the auto layout is working. If it says block, the frame lost its layout mode.
  2. Position: Does any child have position: absolute? If yes, that child was set to absolute in Figma, and it is out of the flex flow.
  3. Gap: Does the container use gap, or does each child have margins? The gap is the correct output. Margins on children indicate a manual workaround.
  4. Sizing: Are the dimensions fixed or auto? A child with flex: 0 0 auto and a fixed width is a fixed item. A child with flex: 1 1 0% is a growing item.

Absolute Positioning Fallback

The layoutPositioning value in the Figma API is the key. It has two states: AUTO and ABSOLUTE. The default is AUTO. The node participates in the auto layout flow. When you check the “Absolute position” checkbox on the canvas, the API value changes to ABSOLUTE, and the exported CSS uses position: absolute. That is the fallback you will rewrite.

Here is what Figma Dev Mode exports for an item with absolute positioning inside an auto layout frame:

.frame {
  display: flex;
  position: relative;
  padding: 24px;
}

.item-absolute {
  position: absolute;
  top: 0;
  left: 0;
  width: 100px;
  height: 100px;
}

The CSS is valid, and it reproduces the visual. But it is brittle. The absolute item is removed from the flex flow. It does not respond to gap, justify-content, or align-items. It is pinned to the frame’s padding box. If the frame grows or the padding changes, the item stays where it was. Rewrite this as a flex item with margin-left: auto or a nested flex container, depending on the intent. Absolute positioning is the last resort, not the first tool.

Auto Layout Component Padding Gap CSS Output

For a design-system author, the critical question is what the component output looks like when you change padding and gap values. Figma auto layout stores padding as four independent numbers: paddingTop, paddingRight, paddingBottom, paddingLeft. The CSS output uses the shorthand padding with four values in the order top, right, bottom, left. The gap is a single number, itemSpacing, which maps to the CSS gap.

How the Box Model Translates

The output is deterministic. Set a button with 8px vertical padding and 16px horizontal padding, and Figma exports padding: 8px 16px. Set different values on each side, and it exports the full four-value shorthand. The order is always top, right, bottom, left. That matches the CSS box model.

What catches developers is the interaction between gap and padding. In CSS, gap creates space between flex items, and padding creates space inside the container. They do not overlap. Figma follows the same rule. But the visual result can confuse: an item with gap: 16px and padding: 24px has 40px between the container edge and the first item. That is correct in both systems.

Negative Gap

A more subtle issue is the negative gap. Figma added negative gap support, which pulls items closer together. The CSS output for a negative gap is gap: -8px. That is valid CSS per the Box Alignment spec, and it works in modern browsers. But it is a rare pattern. If you inherit a component with a negative gap, verify that the target browsers support it. The gap in flexbox shipped later than grid gap, so older engines may ignore it. Check caniuse for current support data before relying on this.

Figma Auto Layout Wrap and Flex-Wrap

A single-direction layout is not always a single line. Figma auto layout added wrap support, which maps to flex-wrap: wrap on the container. The export sets flex-wrap, and items flow to the next line when they exceed the container width. The gap applies between lines as well as between items. That matches the CSS row-gap and column-gap behaviour.

Wrap has a specific cost. It changes how the browser computes layout. A wrapping flex container must measure every item on every line to determine the line breaks. That is a layout pass, not a paint pass, and it compounds with image loading. If your auto layout frame wraps a list of images, the browser may reflow the entire container each time an image loads and the width changes. Set explicit dimensions on the images or use aspect-ratio.

Here is the CSS output for a wrapping auto layout frame with a fixed width and flex-wrap: wrap:

.gallery {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.item {
  flex: 0 0 180px;
}

That is the same code you would write for a flexbox gallery. The difference is that you might choose CSS Grid for this pattern instead. Grid with grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) produces the same visual with less code and better alignment across rows. The export cannot make that choice. It only emits what the visual shows.

What the Export Gets Wrong: Alignment and Distribution

The alignment values are the most common source of silent errors. Figma’s primaryAxisAlignItems maps to justify-content, and counterAxisAlignItems maps to align-items. The values align: MIN becomes flex-start, CENTER becomes center, MAX becomes flex-end. But the distribution value SPACE_BETWEEN has a catch.

The Collapsed Space-Between Fix

When you set SPACE_BETWEEN in Figma, the export writes justify-content: space-between. That is correct for a fixed-width container. But if the container is set to AUTO width, the items shrink to their content width. The space-between distribution has nothing to distribute. The visual collapses to a tight group. The export does not warn you. It produces the CSS and leaves the problem invisible.

Set the frame width to FIXED before you use SPACE_BETWEEN. That is a Figma-side change, not a CSS-side change. If you inherit a component with space-between and collapsed items, check the frame width first. You cannot fix it from the CSS alone without changing the design.

The Counter Axis Trap

Alignment also fails when the counter axis is set to AUTO. Figma’s counterAxisSizingMode controls this. If the counter axis is AUTO, the container grows to fit its children. align-items has no visible effect because the container is exactly as tall as the content. The export is correct. The visual is not what you expect. Set the counter axis to FIXED if you want the alignment to have room to work.

When You Should Not Use Auto Layout

Auto layout is the wrong tool for any layout that has two axes of alignment. A pricing table with columns and rows. A card grid where each card needs to align with the card above and beside it. A page skeleton with a sidebar and main region. All of these are grid territories. Using auto layout for them produces nested flexbox that is harder to maintain and less explicit than a single grid declaration.

The rule: if you need to align items across rows and columns simultaneously, use CSS Grid. If you need to distribute items along one axis, use flexbox. Figma auto layout is a flexbox editor. It cannot express the two-axis case. The export quality is not the problem. The model is.

When you inherit a Figma file with a nested auto layout grid, rewrite it. The conversion is straightforward, and the result is usually shorter and clearer. The nested flexbox version is not wrong. It carries more structural weight than necessary. Grid gives you the row and column tracks explicitly, and the gap applies uniformly in both directions.

FAQ

Does Figma auto layout export CSS Grid? No. Auto layout only exports flexbox. The layoutMode value supports horizontal, vertical, or none. There is no grid mode. For two-axis layouts, write the CSS Grid yourself.

Why does my space-between layout collapse? The frame width is set to AUTO. Space-between needs a fixed-width container to distribute the free space. Set the primary axis sizing to FIXED in Figma, or the export produces justify-content: space-between with no effect.

Is absolute positioning inside auto layout a good idea? Rarely. An absolutely positioned item is removed from the flex flow. It does not respond to gap or alignment. It is pinned to the frame. Use a flex item with margin-left: auto instead, or a nested flex container.

Can I use negative gap in production? Yes, but verify browser support. The gap in flexbox is well-supported in modern engines, but negative values are a newer spec addition. Check caniuse for the target browsers before relying on it.

The Honest Caveat

Auto layout is a visual flexbox editor, and it is good at that. The export quality is high for one-axis layouts because the underlying model matches. The failure case is the two-axis layout, where the tool cannot help you. That is not a Figma limitation. It is the difference between a line and a plane. Treat auto layout output as a draft, not a final artifact. The export tells you what the design looks like, not what the CSS should be. The gap between those two is where you do your real work.