responsive-props

Responsive props for Styled Components

Usage no npm install needed!

<script type="module">
  import responsiveProps from 'https://cdn.skypack.dev/responsive-props';
</script>

README

codecov Travis Commitizen friendly semantic-release Greenkeeper badge

Responsive Props 💅

Responsive props for Styled Components.

alt text

responsive-props is a HOC that enhances a styled component with a responsive API, to handle styling based on any number of media queries.

This is useful when a styled component needs to have different styles based on one or more media queries, in different contexts of an application.

A common example of this is a column in a grid, where a column can have a different widths depending on a matching media query. For this particular use case, responsive-props can provide the following API:

// Possibility to target specific media queries and apply styles accordingly
<Row>
  <Column span={{ m: 6, l: 4, xl: 3 }} />
</Row>
// Or pass a single value that will apply styles without any media queries
<Row>
  <Column span={6} />
</Row>

The above example is take from the Styled Flexbox Grid library, which uses responsive-props.

Installation

yarn add responsive-props
# or
npm install responsive-props

Basic Example

The default export of responsive-props is a HOC that takes two parameters. The first parameter is the component to be wrapped/enhanced, and the second parameter is an object containing functions/mixins, that will generate styles for a given media query.

import React from "react";
import styled, { css } from "styled-components";
// responsive-props HOC
import withResponsiveProps from "responsive-props";

// Create a styled-components
const StyledComponent = styled.div`
  width: 200px;
  height: 200px;
  background: palevioletred;

  // Styles returned from mixins targeting specific media queries will be added here
  ${({ responsiveProps }) => responsiveProps}
`;

// Define the mixin `background`
const background = bg => css`
  background: ${bg};
`;

// Wrap `StyledComponent with the `responsive-props` HOC
const WrappedStyledComponent = withResponsiveProps(
  // Wraps `StyledComponent
  StyledComponent,
  {
    // registers ´background` as a mixin
    background: background
  }
);

// Define the breakpoints, passed to `WrappedStyledComponent` `breakpoints` prop bellow
const breakpoints = { xs: 320, s: 576, m: 768, l: 992, xl: 1200 };

// WrappedStyledComponent can now be used in the following way
const Example = () => (
  <WrappedStyledComponent
    background={{ s: "#002635", m: "#013440", l: "#AB1A25" }}
    breakpoints={breakpoints}
  />
);

export default Example;

The above component WrappedStyledComponent, will result in a div in the shaped of a square, with different background colors depending on what media query is matching.

alt text

Notice how different breakpoint are targeted inside of the background prop of WrappedStyledComponent. The value for each breakpoint (#002635, #013430 and #AB1A25) will be passed as the bg parameter of the background mixin, and generate corresponding styled for each media query.

<WrappedStyledComponent
  background={{ s: "#002635", m: "#013440", l: "#AB1A25" }}
  breakpoints={breakpoints}
/>

Also notice that the name of the prop background, matches that of the key background in the object of mixins that is passed to the withResponsiveProps HOC.

const WrappedStyledComponent = withResponsiveProps(
  // Wraps `StyledComponent
  StyledComponent,
  {
    // registers ´background` as a mixin
    background: background
  }
);

Important: The line ${({ responsiveProps }) => responsiveProps} is where styles of each media queries will be inserted. Without this no media queries will be applied to the styled component.


It is possible to register any number of breakpoints with a naming convention of your choice. In the above examples we used the naming convention: xs, s, m, l, xl. However if you for example prefer the convention used by Twitter Bootstrap (xs, sm, md, lg, xl) you could configure the breakpoints like this:

// Define the breakpoints, passed to `WrappedStyledComponent` `breakpoints` prop bellow
const breakpoints = { xs: 320, sm: 576, md: 768, lg: 992, xl: 1200 };

// WrappedStyledComponent can now be used in the following way
const Example = () => (
  <WrappedStyledComponent
    background={{ sm: "papayawhip", md: "palevioletred", lg: "#AB1A25" }}
    breakpoints={breakpoints}
  />
);

Register breakpoints

There are two ways to register the breakpoints for components enhanced by responsive-props.

The first (which has already been demonstrated in the Basic Example is to pass to pass an object of breakpoints to the enhanced component via the breakpoints prop. The other more convenient way is to register the breakpoints inside a theme of the styled-components ThemeProvider.

Via <ThemeProvider /> (recommended)

Putting the breakpoints inside a theme has the benefit of only having to define them once, and not having to remember to include for every component. The breakpoints should be put under the namespace responsiveProps.breakpoints of the theme, as demonstrated bellow.

import React from 'react';
import { ThemeProvider } from 'styled-components';
const theme = {
  responsiveProps: {
    breakpoints: {
      xs: 320,
      s: 576,
      m: 768,
      l: 992,
      xl: 1200
    }
  }
}

// The `breakpoints` prop from the `Basic Example` can now be omitted
const Example = () => (
  <ThemeProvider theme={theme}>
    <WrappedStyledComponent background={{ s: '#002635', m: '#013440', l: '#AB1A25' }} />
  <ThemeProvider theme={theme}>
);

Of course this is a contrived example where the benefit of theme isn't very clear. Normally the ThemeProvider would be placed somewhere higher up the component three, where any component inside the ThemeProvider can omit the breakpoints prop.

Via the breakpoints prop

If your application doesn't make use of ThemeProvider (or you would like to override the default breakpoints provided by theme) it is possible to pass breakpoints via the breakpoints prop, of a component enhanced by responsive-props.

<WrappedStyledComponent breakpoint={{ xs: 320,s: 576,m: 768,l: 992, xl: 1200 }} >

API

The API of a component enhanced by responsive-props is centered around the format of the value, that is passed to the props of the component. They can either be in the form of an object to target specific media queries or a single value to seamlessly act as regular adaptational props.

In React terms the pattern implemented by responsive-props is known as Props Proxy.

Target specific media queries

To target a specific breakpoint an object of breakpoints is passed as the value of the prop. The keys of the of object should be present in the registered breakpoints in order to work.

Bellow the keys (s, m, and l) are the breakpoints to target. The values (#002635, #013440 and #AB1A25) are parameter that will be passed to a mixin/function namned background, responsible for generating styles for the specified breakpoints.

<WrappedStyledComponent
  background={{ s: "#002635", m: "#013440", l: "#AB1A25" }}
/>

This will result in different background colors (#002635, #013440 and #AB1A25) for each breakpoint (s, m, and l).

Single value (without media queries)

If a style should be applied without media queries (i.e. independent of the bowser/viewport width) it is possible to only pass a single value to the same prop, instead of an object of breakpoints.

<WrappedStyledComponent background="#002635" />

This will result in a background color of #002635 for all viewports.

Mixins with multiple parameters

If a mixin accept more than one parameter it is possible to pass the parameter for a given breakpoint in the form on an array. In the example bellow the verticalPadding mixin takes two parameters, the first for top padding and the second for bottom padding.

<WrappedStyledComponent verticalPadding={{ s: ['2rem', '1rem'], m: ['3rem', '2rem'] }}

More detailed snippet of the above example:

// Define the mixin `background`
const verticalPadding = (paddingBottom, paddingTop = 0) => css`
  padding: ${paddingTop} 0 ${paddingBottom} 0;
`;

// Wrap `StyledComponent with the `responsive-props` HOC
const WrappedStyledComponent = withResponsiveProps(
  // Wraps `StyledComponent`
  StyledComponent,
  {
    // registers `verticalPadding` as a mixin
    verticalPadding: verticalPadding
  }
);

// WrappedStyledComponent can now be used in the following way
const Example = () => (
  <WrappedStyledComponent
    verticalPadding={{ s: ["2rem", "1rem"], m: ["3rem", "2rem"] }}
  />
);

v2 migration instructions

innerRef and nodeRef is deprecated as of responsive-props v2. Instead V2 implements the new React.forwardRef API and expects the use of ref prop to get the underlaying DOM element.

responsive-props v2 is compatible with styled-components: ^4.0.0 and react ^16.3.0. Make sure to match these dependencies or consider using an older version.

If you wish to use styled-components v3 use version 1.2.1 of responsive-props. Note that if you're using later versions of React this may throw warnings, updating is recommended.