styled-components: gap property not working (React Native)

If I want to set a specific space between items on a flex container, I still need to set individual margin and/or padding to all children instead of simple using the new gap property on the flex container.

Something like this should work (center all MyFlexContainer children and set 10px between each):

import { View } from 'react-native';
import styled from 'styled-components/native';

const MyFlexContainer = styled.View`
  flex-direction: column;
  justify-content: center;
  flex: 1;
  gap: 10px;
`

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 30
  • Comments: 28 (1 by maintainers)

Most upvoted comments

Sadly there is no gap support yet for React Native.

Make it

Here’s my approach.

import { StyleSheet, View, Dimensions } from 'react-native'

export default () => {
  return (
    <View style={styles.container}>
      <View style={styles.child} />
      <View style={styles.child} />
      <View style={styles.child} />
      <View style={styles.child} />
      <View style={styles.child} />
      <View style={styles.child} />
    </View>
  )
}

const gap = 10
const itemPerRow = 4
const totalGapSize = (itemPerRow - 1) * gap
const windowWidth = Dimensions.get('window').width
const childWidth = (windowWidth - totalGapSize) / itemPerRow

const styles = StyleSheet.create({
  container: {
    flexWrap: 'wrap',
    flexDirection: 'row',
    marginVertical: -(gap / 2),
    marginHorizontal: -(gap / 2)
  },
  child: {
    width: childWidth,
    height: childWidth,
    marginVertical: (gap / 2),
    backgroundColor: '#52B596',
    marginHorizontal: (gap / 2)
  }
})

Here is what I have been doing to simulate gaps. I basically create a margin on the children and then negate the margin with a negative padding on the parent. The only margin that remains is the one between the children. The reason why I divide the margin and padding by 2 is to stay true to the gap between two children set in the gap constant.

const Component = () => {
  return (
    <View style={styles.container}>
      <View style={styles.child}></View>
      <View style={styles.child}></View>
    </View>
  );
};

// Gap you want to achieve
const gap = 8;

const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    paddingHorizontal: (gap / -2),
  },
  child: {
    marginHorizontal: gap / 2,
  },
});

Sadly there is no gap support yet for React Native.

+1 for implementing the gap attribute. Putting styles on each child component is not all that maintainable

Weird comments aside, I can share what I use for this. It has an imperfection though.

This is the API:

<Gap gap={1}>
  <Child />
  <Child />
</Gap>

It also supports fragments and conditional components:

<Gap gap={1}>
  <>
    {active && <Child />}
    <Child />
  </>
</Gap>

You can also use a divider component:

<Gap divider={<View />}>
  <Child />
  <Child />
</Gap>

Here is the code:

import flattenChildren from 'react-keyed-flatten-children'
import { View, ViewStyle } from 'react-native'
import { Children } from 'react'

type Props = {
  gap?: number
  direction?: 'vertical' | 'horizontal'
  style?: ViewStyle
  flex?: number
  divider?: React.ReactNode
}

export function Gap({
  gap = 4,
  direction = 'vertical',
  style,
  flex =  direction == 'horizontal' ? 1 : undefined,
  divider
}) {
  const kids = flattenChildren(children)
  const flexDirection = direction == 'vertical' ? 'column' : 'row'

  const styleKey = direction == 'vertical' ? 'height' : 'width'

  return (
    <View style={[{ flexDirection }, style]}>
      {Children.map(kids, (child, index) => {
        return (
          <>
            {index > 0
              ? divider || <View style={{ [styleKey]: gap }} />
              : null}
            <View style={{ flex }}>{child}</View>
          </>
        )
      })}
    </View>
  )
}

The only downside of this code is that, if the child component returns null, it will still add a gap. This is a limitation of React’s top-down approach.

We need this first

facebook/yoga#1116

yoga pr is completed now

I confirm gap support is working well with styled-components after upgrading to RN 0.71!

Can confirm that the gap property is working in React Native 0.71 https://reactnative.dev/docs/layout-props#gap

I used the upgrade helper (for my app it was 0.70.6 -> 0.71) here https://react-native-community.github.io/upgrade-helper/?from=0.70.6&to=0.71.0

Update the component from @ozgrozer above a bit. So it is more reusable. Don’t mind the memo if you don’t need it.

import React, { Children, memo, ReactNode, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import { Metrics } from '~/app/models/Metrics';

type Props = {
  gap: number;
  itemPerRow: number;
  children: ReactNode;
  childrenAreSquared?: boolean;
};

export const Grid = memo(({ gap, itemPerRow, children, childrenAreSquared }: Props) => {
  const [containerWidth, setContainerWidth] = useState(0);
  const totalGapSize = itemPerRow * gap;
  const childWidth = (containerWidth - totalGapSize) / itemPerRow;
  const childHeight = childWidth;

  const Styles = StyleSheet.create({
    Container: {
      flexDirection: 'row',
      flexWrap: 'wrap',
      marginHorizontal: -(gap / 2),
      marginVertical: -(gap / 2),
    },
    Child: {
      ...(childrenAreSquared && { height: childHeight }),
      marginHorizontal: gap / 2,
      marginVertical: gap / 2,
      width: childWidth,
    },
  });

  return (
    <View style={Styles.Container} onLayout={e => setContainerWidth(e.nativeEvent.layout.width)}>
      {Children.map(children, (child, index) => (
        <View key={index} style={Styles.Child}>
          {child}
        </View>
      ))}
    </View>
  );
});

I personally use a Gap component

export function Gap({ size, direction = 'horizontal' }: Props) {
  return <View style={{ [direction === 'horizontal' ? 'width' : 'height']: size }} />;
}

<Playground>
  <Cat />
  <Gap size={12} />
  <Dog />
<Playground/>

For those stumbling on this thread; I realized gap does not work on Text elements for what I was doing. I had to refactor a bit such that I could use gap on Stack or something else.