@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
890 lines (783 loc) • 23.4 kB
Markdown
---
title: 'Space'
description: 'The Space component provides margins within the provided spacing patterns.'
version: 11.3.0
generatedAt: 2026-05-19T08:46:53.017Z
checksum: 090b7d977ba4be5e2c4c04d199a30a4048416c59f443a56985df2f80629d9c40
---
# Space
## Import
```tsx
import { Space } from '@dnb/eufemia'
```
## Description
The Space component provides `margins` and inner `padding` within the [provided spacing patterns](/uilib/usage/layout/spacing#spacing-helpers).
## Relevant links
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/space)
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/layout/space)
The reason this exists is to make your syntax as clean as possible. This way, you see directly in words what the spacing is for every affected component.
### Spacing Table
| Pixel | Type | Rem | CSS Variable |
| ----- | ---------- | ------- | -------------------- |
| 8 | `x-small` | **0.5** | `--spacing-x-small` |
| 16 | `small` | **1** | `--spacing-small` |
| 24 | `medium` | **1.5** | `--spacing-medium` |
| 32 | `large` | **2** | `--spacing-large` |
| 48 | `x-large` | **3** | `--spacing-x-large` |
| 56 | `xx-large` | **3.5** | `--spacing-xx-large` |
**NB:** In some circumstances you may be in need of using **0.25rem** (4px) - therefore `xx-small` also exists, but as a single type. So, combining `xx-small` and `small` would not result in 0.25rem, but still remain 1rem.
### Value Format
There are a couple of different ways you can define the spacing types and values:
- **Types:** `small small x-small` (combine types up to _10rem_)
- **number:** `2.5` (equivalent to `rem`)
- **string(rem):** `2.5rem`
- **string(px):** `40px` (gets converted to `rem`)
- **boolean:** `true` (equivalent to `small`), `false` (equivalent to `zero`)
To get a spacing of e.g. **2.5rem** (40px), you may combine types `large` and `x-small`.
```tsx
{
/* All of these methods will result in the same spacing */
}
;<Space top="large x-small" right="2.5" bottom="2.5rem" left="40px" />
```
With React, you can also use an object with the different directions:
```tsx
{
/* All of these methods will result in the same spacing */
}
;<Space
space={{
top: 'large x-small',
right: '2.5',
bottom: '2.5rem',
left: '40px',
}}
/>
```
### Components and Spacing
Every component supports the spacing patterns, so it's possible to send in the `top`, `right`, `bottom`, `left` and `space` properties directly, like:
```tsx
<Button top="large x-small medium" />
<Button
space={{
top: 'large x-small medium',
}}
/>
```
### Spacing shorthands
A shorthand for getting 1rem (most used) is to simply send in a boolean set as true. No given value in JSX means true, so you only need the property key:
```tsx
{
/* Equivalent to top="small" */
}
;<Button top />
{
/* Equivalent to top="small" right="small" bottom="small" left="small" */
}
;<Button space />
```
In order to set all four directions at once, you can provide a string as the `space` value:
```tsx
render(<Button space="large x-small medium" />)
```
### Does it not work as expected?
Is `margin` not giving the expected spacing? That may be due to **Margin Collapsing**. Margins collapse in the following situations:
- Adjacent siblings
- Completely empty boxes
- Parent and first or last child element
The best solution is to only use one direction of margins, e.g. `bottom`. Or you can set the [collapse property](/uilib/layout/space/properties) to `false`.
### Margin collapsing
In order to help handle unwanted margin collapsing in typography elements, see [this example](/uilib/elements/heading#example-of-margin-collapsing).
### Conditional Reset
For resetting spacing (`margin: 0`) only when no spacing is defined, you can make use of `dnb-space__reset`.
### Style and Spacing
Every Eufemia component that supports spacing props uses CSS custom properties (e.g. `--margin-t-s`) on the `style` attribute to drive responsive margins. When you pass a `style` prop to a component, your styles and the spacing styles are merged together — spacing properties take precedence.
This means you can safely combine your own styles with spacing:
```tsx
<Space style={{ color: 'var(--color-sea-green)' }} top="medium">
...
</Space>
```
If you work with raw DOM elements and set styles via `setAttribute('style', ...)`, make sure you preserve any existing style values when adding new ones, so the spacing custom properties are not lost.
```js
const existing = element.getAttribute('style')
const merged = existing
? `${existing.replace(/;?\s*$/, '')}; ${style}`
: style
element.setAttribute('style', merged)
```
### Responsive spacing
**NB**: This feature is in beta and may be subject to change.
Wrap a section of your UI in `Space.ResponsiveContext` to enable spacing that adapts automatically across breakpoints. Components inside the wrapper that use `useSpacing` will receive a `dnb-space-responsive--<density>` CSS class, which remaps static `--spacing-*` values to the responsive `--responsive-spacing-*` custom properties below.
This is useful when you want consistent, viewport-aware gaps between elements without managing breakpoint-specific spacing props on every component.
See the [Responsive layout gap demo](/uilib/layout/space/demos/#responsive-layout-gap) for a live example.
| CSS Variable | Compact `← small` | Basis `small → medium` | Spacious `medium →` |
| ------------------------------- | ----------------- | ---------------------- | ------------------- |
| `--responsive-spacing-xx-small` | 0.25rem (4px) | 0.5rem (8px) | 1rem (16px) |
| `--responsive-spacing-x-small` | 0.5rem (8px) | 1rem (16px) | 1.5rem (24px) |
| `--responsive-spacing-small` | 1rem (16px) | 1.5rem (24px) | 2rem (32px) |
| `--responsive-spacing-medium` | 1.5rem (24px) | 2rem (32px) | 2.5rem (40px) |
| `--responsive-spacing-large` | 2rem (32px) | 2.5rem (40px) | 3rem (48px) |
| `--responsive-spacing-x-large` | 2.5rem (40px) | 3rem (48px) | 3.5rem (56px) |
| `--responsive-spacing-xx-large` | 3rem (48px) | 3.5rem (56px) | 4rem (64px) |
All `--responsive-spacing-*` CSS variables are scoped to the `.dnb-space` class as of now.
## Demos
### Spacing method #1
`Space` component. The RedBox is only to visualize the result.
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="spacing-method-space"
scope={{
RedBox,
}}
>
<RedBox>
<Space top="large x-small">
<Input label="Input" />
</Space>
</RedBox>
</ComponentBox>
</TestStyles>
)
```
### Spacing method #2
Define the space directly.
```tsx
render(
<TestStyles>
<ComponentBox data-visual-test="spacing-method-component">
<Input label="Input A" bottom="small" />
<Input label="Input B" />
</ComponentBox>
</TestStyles>
)
```
### Spacing method #3
Using the `useSpacing` hook.
```tsx
render(
<TestStyles>
<ComponentBox
scope={{
RedBox,
useSpacing,
}}
data-visual-test="spacing-method-form-row"
>
{() => {
const Component = ({
className = null,
style = null,
...props
}) => {
const params = useSpacing(props, {
...props,
className: `my-component dnb-space ${className || ''}`.trim(),
style,
})
return <div {...params} />
}
return (
<>
<RedBox>
<Component top="small medium large">Space A</Component>
</RedBox>
<RedBox>
<Component top>Space B</Component>
</RedBox>
<RedBox>
<Component innerSpace="large">Inner Space</Component>
</RedBox>
<RedBox>
<Component
innerSpace={{
large: true,
}}
>
Has space when breakpoint is large
</Component>
</RedBox>
</>
)
}}
</ComponentBox>
</TestStyles>
)
```
## Responsive `space`
The `space` property supports [media query breakpoints](/uilib/usage/layout/media-queries) (`small`, `medium`, `large`) for responsive spacing. Provide an object with breakpoint keys to apply different values at each screen size.
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="responsive-outer-spacing"
scope={{
RedBox,
}}
>
<RedBox>
<Space
space={{
small: 'large x-small',
medium: {
top: '2rem',
left: '16px',
bottom: 'large',
right: '5rem',
},
large: true,
}}
>
<P>Content</P>
</Space>
</RedBox>
</ComponentBox>
</TestStyles>
)
```
### Responsive `innerSpace`
The `innerSpace` property controls padding inside the Space component. It shares the same API as `space`.
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="inner-spacing"
scope={{
RedBox,
}}
>
<RedBox>
<Space
innerSpace={{
small: 'large x-small',
medium: true,
large: {
top: '2rem',
left: '16px',
bottom: 'large',
right: '5rem',
},
}}
>
<P>Content</P>
</Space>
</RedBox>
</ComponentBox>
</TestStyles>
)
```
### `inline` and `block` shorthand
Both `space` and `innerSpace` properties support `inline` and `block` shorthand properties for more semantic spacing control.
- `inline` applies spacing to left and right (horizontal)
- `block` applies spacing to top and bottom (vertical)
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="space-inline-block"
scope={{
RedBox,
}}
>
{/* Basic inline/block usage for space (margin) */}
<Space
space={{
inline: 'small',
block: 'large',
}}
>
<RedBox>
space: inline=small (left/right), block=large (top/bottom)
</RedBox>
</Space>
{/* Basic inline/block usage for innerSpace (padding) */}
<Space
innerSpace={{
inline: 'medium',
block: 'x-small',
}}
>
<RedBox>
innerSpace: inline=medium (left/right), block=x-small
(top/bottom)
</RedBox>
</Space>
{/* Combining both space and innerSpace with inline/block */}
<Space
space={{
block: 'large',
}}
innerSpace={{
inline: 'medium',
block: 'small',
}}
>
<RedBox>
Combined: space block=large + innerSpace inline=medium,
block=small
</RedBox>
</Space>
{/* Media queries with inline/block for both properties */}
<Space
space={{
small: {
inline: 'x-small',
},
medium: {
block: 'medium',
},
large: {
inline: 'large',
block: 'small',
},
}}
innerSpace={{
small: {
block: 'x-small',
},
medium: {
inline: 'small',
},
large: {
inline: 'medium',
block: 'large',
},
}}
>
<RedBox>
<div>Responsive inline/block for both space and innerSpace</div>
<div>Different combinations per breakpoint</div>
</RedBox>
</Space>
{/* Mixing inline/block with traditional directional props */}
<Space
space={{
inline: 'small',
}}
top="x-large"
innerSpace={{
block: 'medium',
}}
>
<RedBox>
Mixed: space inline + top override, innerSpace block
</RedBox>
</Space>
</ComponentBox>
</TestStyles>
)
```
### Responsive layout gap
**NB**: This feature is in beta and may be subject to change.
Use `Space.ResponsiveContext` to preview responsive spacing in practice. The default `basis` density follows viewport breakpoints.
See the [responsive spacing](/uilib/layout/space#responsive-spacing) table for the specific values.
In this example, `gap` and `space` values adjust automatically based on the viewport size (applies also to `inline`, `block`, `top`, `right`, `bottom`, and `left`).
```tsx
render(
<Space.ResponsiveContext>
<Section
innerSpace={{
block: 'medium',
}}
breakout={false}
surface="dark"
>
<Flex.Stack
space={{
inline: 'large',
}}
gap="small"
>
<Heading size="x-large">Heading</Heading>
<P>My spacing adjusts responsively</P>
<Space.ResponsiveContext off>
<P>My spacing stays fixed</P>
</Space.ResponsiveContext>
</Flex.Stack>
</Section>
</Space.ResponsiveContext>
)
```
### Spacing with no margin collapse, due to the flex usage
```tsx
render(
<TestStyles>
<ComponentBox
hideCode
scope={{
RedBox,
Vertical,
}}
>
<Vertical>
<RedBox>
<Space bottom="small">
<>
I have <code className="dnb-code">bottom="small"</code>
</>
</Space>
</RedBox>
<RedBox>
<Space top="large">
<>
I have <code className="dnb-code">top="large"</code>
</>
</Space>
</RedBox>
</Vertical>
</ComponentBox>
</TestStyles>
)
```
### All four values will result in an equivalent margin
```tsx
render(
<TestStyles>
<ComponentBox data-visual-test="spacing-margins" hideCode>
<Space top="large x-small" right="2.5" bottom="2.5rem" left="40px">
<details>
<summary>
I have four <code className="dnb-code">2.5rem</code> margins!
</summary>
And this are my CSS classes:{' '}
<code className="dnb-code">
dnb-space dnb-space__top--large dnb-space__top--x-small
dnb-space__right--large dnb-space__right--x-small
dnb-space__bottom--large dnb-space__bottom--x-small
dnb-space__left--large dnb-space__left--x-small
</code>
</details>
</Space>
</ComponentBox>
</TestStyles>
)
```
### Visual space testing
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="spacing-patterns"
scope={{
MagicBox,
CustomStyle,
}}
hideCode
>
{() => {
const TestCase = (props) => {
return (
<CustomStyle {...props}>
{listOfBoxes.map((v) => (
<Space key={v} top={v}>
<MagicBox />
</Space>
))}
</CustomStyle>
)
}
const listOfBoxes = []
for (let i = 0, c = 0, l = 20; i <= l; i++) {
listOfBoxes.push(String(c))
c += 0.5
}
return (
<div className="spacing-patterns">
<P bottom>
With <Code>dnb-core-style</Code>
</P>
<TestCase className="dnb-core-style" />
<P top bottom>
Without
</P>
<TestCase />
</div>
)
}}
</ComponentBox>
</TestStyles>
)
```
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="spacing-elements"
scope={{
MagicBox,
CustomStyle,
}}
hideCode
>
{() => {
const listOfBoxes = []
for (let i = 0, c = 0, l = 10; i <= l; i++) {
listOfBoxes.push(String(c))
c += 1
}
const TestCase = (props) => {
return (
<CustomStyle {...props}>
{listOfBoxes.map((v) => (
<Button
key={v}
left="x-small"
top={v}
size="small"
customContent={<MagicBox />}
/>
))}
</CustomStyle>
)
}
return (
<div className="spacing-elements">
<P bottom>
With <Code>dnb-core-style</Code>
</P>
<TestCase className="dnb-core-style" />
<P top bottom>
Without
</P>
<TestCase />
</div>
)
}}
</ComponentBox>
</TestStyles>
)
```
```tsx
const BlueBox = styled.div`
display: inline-block;
padding: 0.5rem;
background: blue;
ul {
background: white;
}
`
render(
<BlueBox>
<Space
element="ul"
top="small"
right="small"
bottom="small"
left="small"
className="dnb-space__reset"
>
<li> </li>
</Space>
</BlueBox>
)
```
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="space-media-queries"
scope={{
RedBox,
}}
>
{/* Different spacing for different breakpoints */}
<Space
space={{
small: 'small',
medium: 'large',
large: 'x-large',
}}
>
<RedBox>
Responsive spacing: small on mobile, large on tablet, x-large on
desktop
</RedBox>
</Space>
{/* Media queries with individual direction objects */}
<Space
space={{
small: {
top: 'small',
bottom: 'medium',
},
medium: {
top: 'large',
bottom: 'x-large',
},
large: {
top: 'x-large',
bottom: 'xx-large',
},
}}
>
<RedBox>Responsive directional spacing</RedBox>
</Space>
{/* Mixing with individual props */}
<Space
space={{
small: 'medium',
medium: 'large',
large: 'x-large',
}}
right="small" // Individual props override space
>
<RedBox>Media space with right override</RedBox>
</Space>
</ComponentBox>
</TestStyles>
)
```
```tsx
render(
<TestStyles>
<ComponentBox
data-visual-test="innerspace-media-queries"
scope={{
RedBox,
}}
>
{/* Different inner spacing for different breakpoints */}
<Space
innerSpace={{
small: 'small',
medium: 'large',
large: 'x-large',
}}
>
<RedBox>
<div>Responsive inner spacing</div>
<div>Content inside has different spacing per breakpoint</div>
</RedBox>
</Space>
{/* Media queries with directional inner spacing */}
<Space
innerSpace={{
small: {
block: 'small',
inline: 'medium',
},
medium: {
block: 'large',
inline: 'x-large',
},
large: {
block: 'x-large',
inline: 'xx-large',
},
}}
>
<RedBox>
<div>Responsive directional inner spacing</div>
<div>Block and inline spacing changes per breakpoint</div>
</RedBox>
</Space>
</ComponentBox>
</TestStyles>
)
```
## Global Properties
These properties are available in many other components and elements.
```json
{
"props": {
"space": {
"doc": "Has to be an object with either: `top`, `right`, `bottom`, `left`, `inline`, or `block`. Also supports media query breakpoints like `{small: \"medium\", medium: \"large\", large: \"x-large\"}` and shorthand directions `inline`/`block`. Use spacing values like: `small`, `1rem`, `1` or `16px`.",
"type": ["object"],
"status": "optional"
},
"top": {
"doc": "Use spacing values like: `small`, `1rem`, `1` or `16px`. Will use `margin-top`.",
"type": ["string", "number", "boolean"],
"status": "optional"
},
"right": {
"doc": "Use spacing values like: `small`, `1rem`, `1` or `16px`. Will use `margin-right`.",
"type": ["string", "number", "boolean"],
"status": "optional"
},
"bottom": {
"doc": "Use spacing values like: `small`, `1rem`, `1` or `16px`. Will use `margin-bottom`.",
"type": ["string", "number", "boolean"],
"status": "optional"
},
"left": {
"doc": "Use spacing values like: `small`, `1rem`, `1` or `16px`. Will use `margin-left`.",
"type": ["string", "number", "boolean"],
"status": "optional"
}
}
}
```
## Component Properties
```json
{
"props": {
"element": {
"doc": "Defines the HTML element used. Defaults to `div`.",
"type": ["string", "React.Element"],
"status": "optional"
},
"stretch": {
"doc": "If set to `true`, then the space element will be 100% in `width`.",
"type": "boolean",
"status": "optional"
},
"inline": {
"doc": "If set to `true`, then `display: inline-block;` is used, so the HTML elements get aligned horizontally. Defaults to `false`.",
"type": "boolean",
"status": "optional"
},
"innerSpace": {
"doc": "Will add a padding around the content. Also supports media query breakpoints like `{small: { top: 'medium' }}` and shorthand directions `inline`/`block`.",
"type": ["object", "string", "number", "boolean"],
"status": "optional"
},
"noCollapse": {
"doc": "If set to `true`, then a wrapper with `display: flow-root;` is used. This way you avoid **Margin Collapsing**. Defaults to `false`. _Note:_ You can't use `inline={true}` in combination.",
"type": "boolean",
"status": "optional"
}
}
}
```
## Space.ResponsiveContext Properties
Wrap components in `Space.ResponsiveContext` to opt into viewport-aware spacing. See the [responsive layout gap demo](/uilib/layout/space/demos/#responsive-layout-gap) for a live example.
```json
{
"props": {
"density": {
"doc": "Forces a specific spacing density for descendants. Overrides `defaultBreakpoint` when set.",
"type": ["\"compact\"", "\"basis\"", "\"spacious\""],
"status": "optional"
},
"defaultBreakpoint": {
"doc": "Sets which breakpoint's spacing scale to use as the default. Default: `medium`.",
"type": ["\"small\"", "\"medium\"", "\"large\""],
"status": "optional"
},
"off": {
"doc": "When `true`, disables responsive spacing for descendants, overriding a parent `Space.ResponsiveContext`. Defaults to `false`.",
"type": "boolean",
"status": "optional"
}
}
}
```
## Zero
Use either `0` or `false` (as a number/boolean or string) to set a `margin` of 0.
## Provider
Also, Provider is supporting the `collapse` property.
```tsx
render(
<Provider
space={{
noCollapse: true,
}}
>
<Space>I do not collapse</Space>
<Space>I do not collapse</Space>
</Provider>
)
```