View

Import
import { View } from "reshaped";
import type { ViewProps, ViewItemProps } from "reshaped";
Storybook

View is the most common layout utility in Reshaped as it provides common functionality required for building layouts. It can be used to apply styles based on the design tokens provided by Reshaped and handles many layout edge cases that you would have to solve yourself when using flexbox directly.

View gives you access to multiple flex-based properties, like direction, gap, align for align-items, justify for justify-content, and wrap. When any of these properties are applied, View automatically turns on flexbox mode and starts behaving like one.

Using these properties should help you compose groups of elements together, as well as build full page layouts. Note that the gap property supports any number value you pass to it and will apply a multiplier of an x1 unit token, which is 4px by default. For example, here gap is 8px:

<View align="center" gap={2} direction="row">
  <Avatar initials="RS" size={12} />
  <Text variant="body-2-1">Reshaped</Text>
</View>

In addition to the flexbox API, you can control the View dimensions using properties like padding, width, height, maxWidth, and maxHeight. Similarly, all of them support number values to be treated as multipliers for the x1 unit token. width, height, maxWidth, and maxHeight also support literal string values, so you can define px, %, vh, and other types of values.

<View
  padding={8}
  width="50%"
  backgroundColor="elevation-base"
  borderColor="neutral-faded"
>
  <View backgroundColor="neutral" height={10} />
</View>

Padding can also be applied individually for every side with paddingTop, paddingBottom, paddingStart, and paddingEnd properties, or with paddingInline and paddingBlock for horizontal and vertical sides. Individual padding values have higher priority than the padding property when used together.

<View paddingTop={4} borderColor="neutral">
  <View backgroundColor="neutral-faded" height={10} />
</View>

When used with text content inside other components, you can control its alignment using the textAlign property.

<View textAlign="center" width="50%">
  It's a fez. I wear a fez now. Fezes are cool. You know how I sometimes have
  really brilliant ideas? I hate yogurt. It's just stuff with bits in.
</View>

When using View on mobile devices or inside other containers, you might want to make it full-width without making the markup more complex. Using the bleed property should help in this case.

Consider the following example, where you have multiple paragraphs of text with a View in between them. To keep a single wrapper for the text, you can apply negative margin to the View with the bleed property.

Similar to gap, bleed supports any number value and works as a multiplier of an x1 unit token. Using bleed will automatically remove side borders and border-radius from the View if any were applied.

<View gap={4}>
  <Text as="p">
    Where'd you get the coconuts? Be quiet! Who's that then? The swallow may fly
    south with the sun, and the house martin or the plover may seek warmer
    climes in winter, yet these are not strangers to our land.
  </Text>
  <View bleed={4}>
    <View backgroundColor="neutral-faded" height={10} />
  </View>
  <Text as="p">
    Oh, ow! It's only a model. We shall say 'Ni' again to you, if you do not
    appease us. The swallow may fly south with the sun, and the house martin or
    the plover may seek warmer climes in winter, yet these are not strangers to
    our land.
  </Text>
</View>

The aspectRatio property changes the ratio of the View and its contents. It supports any float value; however, it's easier to pass as a division of numbers, like 4 / 3. You can use it alongside other layout properties defining the size.

<View
  backgroundColor="neutral-faded"
  aspectRatio={16 / 9}
  width="200px"
  borderRadius="medium"
/>

View provides support for positioning elements on the page, offering position, zIndex, and inset properties.

For positions, it supports relative, absolute, fixed, sticky, and static. Together with a position value, you can start using the inset property to assign its distance from each side of the parent as a base token multiplier. Alternatively, you can assign those values individually with insetTop, insetBottom, insetStart, and insetEnd properties.

<View
  position="relative"
  backgroundColor="neutral-faded"
  height="100px"
  borderRadius="medium"
>
  <View position="absolute" insetTop={2} insetEnd={2}>
    <Button icon={IconHeart} />
  </View>
</View>

You can then assign a zIndex value if you need to layer multiple elements positioned next to each other.

When rendering lists, one common use case is to divide them with separators, so View provides the divided property out of the box. It automatically respects the direction of the View and works as expected with hidden items.

<View
  divided
  gap={4}
  padding={4}
  direction="row"
  backgroundColor="base"
  borderColor="neutral-faded"
>
  <View width="40px" height="40px" backgroundColor="neutral-faded" />
  <View width="40px" height="40px" backgroundColor="neutral-faded" />
  <View width="40px" height="40px" backgroundColor="neutral-faded" />
</View>

View applies flexbox rules directly to its children and doesn't add any additional markup to the component you provide. However, you can still use View.Item compound component directly if you need to access additional properties.

Similar to the flexbox API, you can make your View child take up all remaining space using the grow property. This will automatically remove flexbox wrapping from the View and will handle shrinking of the other children correctly when text content inside them goes multiline.

<View direction="row" gap={3}>
  <View
    width="80px"
    height="80px"
    borderRadius="medium"
    backgroundColor="base"
    borderColor="neutral-faded"
  />
  <View.Item grow>
    I am the Doctor, and you are the Daleks! It's a fez. I wear a fez now. Fezes
    are cool. You know when grown-ups tell you 'everything's going to be fine'
    and you think they're probably lying to make you feel better?
  </View.Item>
</View>

In cases when you want to grow an item but also need to apply additional View styles to it, you can apply grow to the View component directly.

<View direction="row" gap={3}>
  <View {...} />
  <View grow backgroundColor="neutral-faded" />
</View>

Unlike in the flexbox API, View lets you override the gap on the View.Item level. This means that when you use the gapBefore property on View.Item, the gap value before this item will be updated, and you won't need multiple View wrappers to achieve custom spacing between the View children.

<View gap={4}>
  <Text variant="title-3">Doctor Who</Text>
  <View.Item gapBefore={0}>
    <Text variant="body-1" color="neutral-faded">
      BBC Series
    </Text>
  </View.Item>
  <Text>
    I'm nobody's taxi service; I'm not gonna be there to catch you every time
    you feel like jumping out of a spaceship. I'm the Doctor, I'm worse than
    everyone's aunt. *catches himself* And that is not how I'm introducing
    myself.
  </Text>
</View>

When building complex layouts, the order property allows you to change the rendering order of the children. This can be very helpful when building responsive layouts.

Note that order works by assigning a weight to an item. The larger the value, the lower the priority it will have during rendering. In the following example, we're setting the order of the second item to 1, which moves it to the end of the list:

<View gap={4} direction="row">
  <View.Item>Item 1</View.Item>
  <View.Item order={1}>Item 2</View.Item>
  <View.Item>Item 3</View.Item>
</View>

View can be used to implement multi-column layouts with a maximum of 12 columns. On every View.Item, you can specify the number of columns this item should take.

You don't need to wrap items into separate rows; instead, they will automatically wrap into the next row once the current row overflows.

<View gap={4} direction="row">
  <View.Item columns={6}>
    <View backgroundColor="neutral-faded" height="40px" />
  </View.Item>
  <View.Item columns={6}>
    <View backgroundColor="neutral-faded" height="40px" />
  </View.Item>
  <View.Item columns={4}>
    <View backgroundColor="neutral-faded" height="40px" />
  </View.Item>
  <View.Item columns={8}>
    <View backgroundColor="neutral-faded" height="40px" />
  </View.Item>
</View>

View works with common styles coming from the design tokens. For example, you can assign backgroundColor and borderColor values to it. Changing the backgroundColor automatically adjusts the text color inside it based on the color contrast ratio.

<View
  width="100px"
  height="100px"
  backgroundColor="primary-faded"
  borderColor="primary-faded"
  justify="center"
  align="center"
>
  View
</View>

The same applies for other tokens and styles, like borderRadius or shadow.

<View
  borderRadius="medium"
  shadow="elevated"
  backgroundColor="elevated"
  borderColor="neutral-faded"
  width="100px"
  height="100px"
  justify="center"
  align="center"
>
  View
</View>

Check the properties table for the full list of available styles and values.

All View layout properties support responsive syntax, which means you can pass an object with values and control its rendering based on the viewport size. We're using a mobile-first approach, which means that you don't have to define a value for every single viewport. Instead, you only need to define the values at which they change, and the component will apply them from smallest to largest.

For example, if you pass { s: "column", l: "row" }, View will use the column direction for small and medium screens. For large and extra-large screens, it will use the row direction.

<View gap={3} direction={{ s: "column", l: "row" }}>
  <View backgroundColor="neutral-faded" height={10} width={10} />
  <View backgroundColor="neutral-faded" height={10} width={10} />
</View>

Properties supporting responsive values:

  • View: gap, align, justify, direction, wrap, height, width, aspectRatio, maxHeight, maxWidth, padding, bleed, position, inset, insetTop, insetBottom, insetStart, insetEnd, borderRadius, borderColor, textAlign
  • View.Item: columns, grow, order, gapBefore

When wrapping View children with the Hidden utility, View will recognize it and remove the additional wrapper element to make flexbox gap work as expected. In the following example, we hide the second item starting with the m viewport size, and gap is resolved correctly:

<View direction="row" gap={3}>
  <View backgroundColor="neutral-faded" height="40px" width="40px" />
  <Hidden hide={{ s: false, m: true }}>
    <View backgroundColor="neutral-faded" height="40px" width="40px" />
  </Hidden>
  <View backgroundColor="neutral-faded" height="40px" width="40px" />
</View>
  • View can be used in many contexts and, therefore, might need to be rendered using specific HTML tags, like ul or ol for lists of items. This can be achieved by using the as property on both View and View.Item.
<View as="ul" gap={2}>
  <View.Item as="li">Item 1</View.Item>
  <View.Item as="li">Item 2</View.Item>
</View>