← ALL NOTES
METHOD·2026.09·6 MIN READ
Choosing Fixed, Hug, and Fill for Figma Components
handoffdesign-systems

This article records the sizing rules I currently use when building components with Figma Auto Layout, especially how I choose between Fixed, Hug, and Fill, and where those choices can behave differently once the design is implemented in code.

I usually start by asking where a dimension comes from. Some values can be derived directly from content and padding. Others are defined by an external component rule or by the parent layout.

Let Derived Sizes Stay Derived

For controls such as buttons, single-line inputs, selects, and tags, height can usually be calculated as:

height = top padding + content height + bottom padding

If the stroke participates in layout, include it as well.

For these components, I usually use Hug. When line-height, padding, or tokens change, the component size updates with them.

A Fixed height introduces another value that must stay in sync. A component can still appear to be 40px tall even after its internal padding has drifted away from the original specification. I have run into this in a real component set: four out of five size variants had padding values that no longer matched the measured Figma values, while their fixed heights still looked correct.

Keeping derived values in the layout system reduces this kind of hidden drift.

What Fixed, Hug, and Fill Represent
FigmaCommon CSS equivalentWhere the size comes from
Fixedwidth: 40px / height: 40pxAn explicit external dimension
Hug contentsauto / fit-contentContent and padding
Fill containerflex: 1 / width: 100%Space allocated by the parent

Each axis can use a different sizing mode. A typical single-line input often uses:

Width = Fill
Height = Hug

The width follows the form container, while the height comes from the text and vertical padding.

Similar concepts appear in other UI systems:

PlatformContent-driven sizingFixed sizing
CSS Flexboxauto / fit-contentExplicit px
FlutterMainAxisSize.minSizedBox(height: 40)
SwiftUIIntrinsic content size.frame(height: 40)
Traditional Android layoutswrap_contentExplicit dp

The APIs differ, but the sizing source is usually the same question.

How I Choose Component Height

Content-driven: use Hug. Common examples include Button, single-line Input, Select, Tag / Chip, Badge, Tooltip, Accordion content, and content cards. Their height is closely tied to text, padding, and content length. Hug keeps those relationships synchronized when the component changes. Multi-line containers benefit from this even more because text length can vary significantly.

Rule-driven: use Fixed. Some dimensions are part of the component specification itself: square icon buttons; avatars; controls that must stay at 32 / 40 / 48px; rows aligned to a fixed vertical rhythm; toolbar or canvas controls with a defined hit area. For a 40 x 40 icon button, changing the icon from 16px to 20px should not change the outer button size. Fixed expresses that rule clearly.

Parent-driven: use Fill. Fill works well when the component should consume available space, for example: form inputs spanning the row; main content areas in split layouts; search fields that take the remaining toolbar space; responsive cards and list items.

I treat width and height independently. Many everyday components use Width = Fill / Height = Hug, while compact icon controls may use Fixed on both axes.

A Useful Check: Can This Value Be Calculated?

When a dimension is unclear, I check whether the current system can already derive it from content, padding, tokens, or parent constraints.

If the value can be calculated consistently, I usually avoid storing an additional Fixed value.

A standalone Fixed dimension makes more sense when it carries a real rule such as touch target size, grid rhythm, business constraints, performance requirements, or external alignment.

Do Not Use Fixed to Hide Token Drift

A common case is a Button and Select placed side by side with slightly different heights. Setting both to 40px will visually align them, but I first check:

  • font size and line-height;
  • vertical padding tokens;
  • border contribution;
  • icon layout.

If both components are intended to follow the same sizing rule, aligning these inputs usually makes their Hug heights match naturally.

Fixed is useful when the component specification explicitly requires a fixed size. It is less useful as a permanent patch for inconsistent internal tokens.

Figma Stroke vs. CSS Border

This is a common source of 1-2px differences between design and implementation.

Figma Stroke can be aligned Inside, Center, or Outside. With an Inside stroke, a 1px line is drawn within the Frame boundary and does not expand the frame size.

CSS border has no equivalent alignment control. Its final size depends on box-sizing and whether an explicit dimension exists.

With an explicit height:

CSS
height: 40px;
border: 1px solid;
box-sizing: border-box;

The outer box stays 40px. The border is included within that dimension, which behaves similarly to an Inside stroke.

With auto height, without an explicit height, the browser derives the final box from its contents:

final height = content + padding + border

In this case, a Figma component that measures 40px with Hug + Inside Stroke can become 42px in CSS if the implementation simply uses content, padding, and a 1px border on both sides.

When pixel-level Inside Stroke matching matters, an inset box shadow can draw the line without taking layout space:

CSS
box-shadow: inset 0 0 0 1px var(--border-color);

This can be useful when the implementation needs to match Figma's Inside stroke dimensions closely. For ordinary product UI, keeping padding, line-height, and border rules consistent is often sufficient.

Engineering Exception: Virtualized Lists

Virtualized lists are a common reason to use Fixed sizing for performance.

Large tables, chat histories, and log viewers often render only the rows around the viewport. The virtualization layer needs to estimate item positions and total scroll height.

Some implementations therefore require a fixed item height, or at least a small set of predictable heights. A row may contain dynamic text while still being constrained by the rendering architecture.

This is worth confirming early. A design that assumes unlimited Hug behavior can diverge significantly from an implementation that needs fixed row heights for virtualization.

Auto Layout and Content-Driven UI

Older design tools were centered on a freeform canvas, where frame dimensions were manually drawn as fixed numbers.

Auto Layout brought content flow, padding, gaps, and parent-child sizing relationships into the design tool, making Figma layouts much closer to real interface layout systems.

Web UI has long relied on content-driven sizing. A basic button without explicit width or height naturally grows with its text and padding.

My current mapping is therefore simple: content determines the size → Hug; parent layout determines the size → Fill; component or product rules define the size → Fixed.

Keeping that relationship consistent between Figma and code makes token changes and responsive behavior much easier to maintain.

Checklist
  • What is the source of this dimension: content, parent, or an explicit rule?
  • Can Button / Input / Select height be derived from line-height and padding?
  • Do related components share the same spacing and typography tokens?
  • Should width and height use different sizing modes?
  • Is Fixed compensating for inconsistent internal tokens?
  • Does Stroke / Border create a 1-2px implementation difference?
  • Would inset box-shadow help when Inside Stroke needs to match exactly?
  • Do virtualization or performance constraints require fixed item heights?
CONTINUE READING
Translating Figma Component Properties into Code Props2026.09Engineering a Figma Design System into Production Assets2026.09