{"id":14384926,"url":"https://github.com/AlecAivazis/redux-responsive","last_synced_at":"2025-08-23T18:31:14.597Z","repository":{"id":57351379,"uuid":"43937315","full_name":"AlecAivazis/redux-responsive","owner":"AlecAivazis","description":"Manage the responsive state of your application using a redux reducer","archived":true,"fork":false,"pushed_at":"2018-08-15T03:18:51.000Z","size":543,"stargazers_count":477,"open_issues_count":6,"forks_count":41,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-05-17T17:21:31.941Z","etag":null,"topics":["react","redux","redux-reducers","responsive"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlecAivazis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-09T06:34:32.000Z","updated_at":"2024-01-12T14:43:01.000Z","dependencies_parsed_at":"2022-08-31T06:22:32.271Z","dependency_job_id":null,"html_url":"https://github.com/AlecAivazis/redux-responsive","commit_stats":null,"previous_names":["aaivazis/redux-responsive"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlecAivazis%2Fredux-responsive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlecAivazis%2Fredux-responsive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlecAivazis%2Fredux-responsive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlecAivazis%2Fredux-responsive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlecAivazis","download_url":"https://codeload.github.com/AlecAivazis/redux-responsive/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":217384805,"owners_count":16168808,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","redux","redux-reducers","responsive"],"created_at":"2024-08-28T18:01:47.087Z","updated_at":"2024-08-28T18:05:50.513Z","avatar_url":"https://github.com/AlecAivazis.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","📦 Legacy \u0026 Inactive Projects"],"sub_categories":[],"readme":"# redux-responsive\n\n[![Build Status](https://travis-ci.org/AlecAivazis/redux-responsive.svg?branch=master)](https://travis-ci.org/AlecAivazis/redux-responsive)\n[![Coverage Status](https://coveralls.io/repos/github/AlecAivazis/redux-responsive/badge.svg?branch=master)](https://coveralls.io/github/AlecAivazis/redux-responsive?branch=master)\n[![npm](https://img.shields.io/npm/v/redux-responsive.svg)](https://www.npmjs.com/package/redux-responsive)\n\nA redux reducer for managing the responsive state of your application.\n\n## Example\n\n```js\n// MyComponent.js\n\nimport React from 'react'\nimport {connect} from 'react-redux'\n\n// grab only the responsive state from the store\n// (assuming you have put the `responsiveStateReducer` under\n//  the key `browser` in your state tree)\nfunction browserSelector({browser}) {\n    return {browser}\n}\n\n@connect(browserSelector)\nclass MyComponent extends React.Component {\n    render() {\n        // grab the responsive state off of props\n        const {browser} = this.props\n\n        let message = `The viewport's current media type is: ${browser.mediaType}.`\n\n        if (browser.lessThan.small) {\n            message += 'Secret message for viewports smaller than than the \"small\" breakpoint!'\n        } else if (browser.lessThan.medium) {\n            message += 'Secret message for viewports between the \"small\" and \"medium\" breakpoints!'\n        } else {\n            message += 'Message for viewports greater than the \"medium\" breakpoint.'\n        }\n\n        return (\n            \u003cp\u003e\n                {message}\n            \u003c/p\u003e\n        )\n    }\n}\n```\n\n## Why Store Responsive State in Redux?\n\nredux-responsive **does not require that you use React as your view library**.  However, since that is what is commonly used alongside redux, this documentation employs common React patterns.\n\nThere are many solutions for cleanly handling responsive designs in React applications. One common approach is to wrap a component in another component which is responsible for handling the behavior and passing the information down as a prop. While this at first seems good and the \"react way\", as the behavior gets more complicated, this quickly leads to a lot of boilerplate code in a single component. Also, depending on the implementation, it is possible that many copies of the responsive wrapper would create many different resize handlers.\n\nUsing a specialized store not only reduces the overall noise in a component, but also guarantees that only a single event listener is listening for resize.\n\n\n## Setup\n\nFirst, add the reducer to the root of your reducer tree (you can name it whatever you want).\n\n```js\n// reducer.js\nimport {combineReducers} from 'redux'\nimport {responsiveStateReducer} from 'redux-responsive'\n\nexport default combineReducers({\n    browser: responsiveStateReducer,\n})\n```\n\nSecond, you must add required event handlers to keep the responsive state up to date. `redux-responsive` uses\n[MediaQueryList](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList)s to efficiently update the\nstore only when required. To do this, use the provided store enhancer.\n\n```js\n// store.js\n\nimport {createStore} from 'redux'\nimport {responsiveStoreEnhancer} from 'redux-responsive'\nimport reducer from './reducer'\n\nconst store = createStore(reducer, responsiveStoreEnhancer)\n\n// or, if you have an initial state for the store\nconst store = createStore(reducer, initialState, responsiveStoreEnhancer)\n\nexport default store\n```\n\nNote that if you are also using some [middlewares](http://redux.js.org/docs/advanced/Middleware.html), the call will look more like this:\n\n```js\nimport {createStore, applyMiddleware, compose} from 'redux'\nimport {responsiveStoreEnhancer} from 'redux-responsive'\nimport reducer from './reducer'\n\nconst store = createStore(\n    reducer,\n    compose(\n        responsiveStoreEnhancer,\n        applyMiddleware(middleware1, middleware2)\n    )\n)\n\n// or, if you have an initial state for the store\nconst store = createStore(\n    reducer,\n    initialState,\n    compose(\n        responsiveStoreEnhancer,\n        applyMiddleware(middleware1, middleware2)\n    )\n)\n\nexport default store\n```\n\nNow your store is ready to use. The store's default breakpoints match common device sizes and are accessible by the following names in your view:\n\n```js\nconst defaultBreakpoints = {\n    extraSmall: 480,\n    small: 768,\n    medium: 992,\n    large: 1200,\n}\n```\n\n# The Responsive State\n\nThe `responsiveStateReducer` (and the reducer returned by `createResponsiveStateReducer`) adds an object with the following keys to the store:\n\n\n- `mediaType`: (*string*) The largest breakpoint category that the browser satisfies.\n- `orientation`: (*string*) The browser orientation. Has three possible values: \"portrait\", \"landscape\", or `null`.\n- `lessThan`: (*object*) An object of booleans that indicate whether the browser is currently less than a particular breakpoint.\n- `greaterThan`: (*object*) An object of booleans that indicate whether the browser is currently greater than a particular breakpoint.\n- `is`: (*object*) An object of booleans that indicate whether the browser is current that particular breakpoint.\n\nFor example, if you put the responsive state under the key `browser` (as is done in the examples above) then you can access the browser's width and current media type, and determine if the browser is wider than the medium breakpoint like so\n\n```js\n// get the current state from the store\nconst state = store.getState()\n\n// browser media type (e.g. \"large\")\nstate.browser.mediaType\n// browser orientation (takes a null value on desktops)\nstate.browser.orientation\n// true if browser width is greater than the \"medium\" breakpoint\nstate.browser.greaterThan.medium\n// true if browser.mediaType === 'small'\nstate.browser.is.small\n```\n\n\n## Using Custom Breakpoints\n\nYou can also create your own reducer based on custom breakpoints:\n\n```js\n// reducer.js\n\nimport {combineReducers} from 'redux'\nimport {createResponsiveStateReducer} from 'redux-responsive'\n\nexport default combineReducers({\n    browser: createResponsiveStateReducer({\n        extraSmall: 500,\n        small: 700,\n        medium: 1000,\n        large: 1280,\n        extraLarge: 1400,\n    }),\n})\n```\n\n\n\n### The Infinity Media Type\n\nWhen the browser is wider than the largest breakpoint, it's `mediaType` value is `infinity`. In order to\nchange this value, add the `infinity` field to the object pass as a second argument to `createResponsiveStateReducer`:\n\n```es6\n// reducer.js\n\nimport {combineReducers} from 'redux'\nimport {createResponsiveStateReducer} from 'redux-responsive'\n\nexport default combineReducers({\n    // passing null to the reducer factory uses the default breakpoints\n    browser: createResponsiveStateReducer(null, {\n        infinity: \"veryBig\"\n    })\n})\n```\n\n\n\n## Adding custom/computed fields to the responsive state\n\nIn some cases, you may want to add computed fields to the responsive state. For example,\nan application may frequently need to know when the browser is `greaterThanOrEqual` to\na particular breakpoint. In order to support this, `redux-responsive` lets you pass a\nfunction and to `createResponsiveStateReducer` arguments as the `extraFields` key.\nThis function will recieve an object with the responsive state and returns an object\nwith the new keys to be injected into the state whenever it is recalculated:\n\n```es6\n// reducer.js\n\nimport {combineReducers} from 'redux'\nimport {createResponsiveStateReducer} from 'redux-responsive'\nimport {transform} from 'lodash'\n\nexport default combineReducers({\n    browser: createResponsiveStateReducer(null, {\n        extraFields: ({ greaterThan, is }) =\u003e ({\n            // greaterThanOrEqual is built by transforming greaterThan\n            greaterThanOrEqual: transform(greaterThan, (result, value, mediaType) =\u003e {\n                // and combining the value with the `is` field\n                result[mediaType] = value || is[mediaType]\n            }, {})\n        }),\n    })\n})\n```\n\n### Tracking window attributes\n\nIn some cases, you may want to have a `window` attributes tracked in your responsive state (for example, `width`).\nTo accomplish this, the first step is to add the custom field as described above.\n\n```es6\n// reducer.js\n\nimport {combineReducers} from 'redux'\nimport {createResponsiveStateReducer} from 'redux-responsive'\nimport {transform} from 'lodash'\n\nexport default combineReducers({\n    browser: createResponsiveStateReducer(null, {\n        extraFields: () =\u003e ({\n            width: window.innerWidth\n        }),\n    })\n})\n```\n\n\nWhen doing this, keep in mind that the responsive state enhancer only causes the\nresponsive state to be recalculated when the browser actually transitions between\nstates. **It does not recalculate on every resize**. Therefore, you might also need\nto add an event handler that recalculates the state at another time:\n\n```es6\n// store.js\n\nimport {calculateResponsiveState} from 'redux-responsive'\n\nconst store = ...\n\nwindow.addEventListener('resize', () =\u003e store.dispatch(calculateResponsiveState(window)))\n```\n\n\n## Server-side Rendering\n\nIsomorphic applications must make sure that the sever-rendered markup matches the\nDOM rendered by the client. Setting the `calculateInitialState` option in the\n`createResponsiveStoreEnhancer` factory method to `false` tells the reducer\nto skip the initial responsive state calculation. The responsive state will\ncontain the default values on both the server and the client side.\n\n```js\n// store/configureStore.js\n\nimport {createStore} from 'redux'\nimport {createResponsiveStoreEnhancer} from 'redux-responsive'\nimport reducer from './reducer'\n\nconst store = createStore(\n                    reducer,\n                    createResponsiveStoreEnhancer({calculateInitialState: false}))\n\nexport default store\n```\n\nThe application should explicitly dispatch the action to recalculate the responsive\nstate when the application is rendered by the client.\n\n\n```jsx\n// client.js\n\n// external imports\nimport ReactDOM from 'react-dom'\nimport {calculateResponsiveState} from 'redux-responsive'\n// local imports\nimport store from 'path/to/store'\n\n// render the application\nReactDOM.render(\n    \u003cProvider store={store}\u003e\n        // ...\n    \u003c/Provider\u003e,\n    document.getElementById('app')\n)\n\n// calculate the initial state\nstore.dispatch(calculateResponsiveState(window))\n```\n\n### Setting the initial media type\n\nIf you know the initial media type for your application (by doing something like looking at\nthe user-agent) you can set the initial media type with the `initialMediaType` key to the\nreducer factory:\n\n```es6\nconst reducer = createResponsiveStateReducer(null, {initialMediaType: 'small'})\n```\n\n## Higher-Order Components\n\nWhen building responsive applications in react, it's common to\nimplement styles for each breakpoint and then apply them like so:\n\n```jsx\nconst commonStyle = {...}\n\nconst styles = {\n    element: {\n        ...commonStyle,\n        color: 'blue',\n    },\n    elementThin: {\n        ...commonStyle,\n        color: 'black',\n    }\n}\n\n// somewhere in your component...\n\n\u003cdiv style={browser.lessThan.medium ? styles.elementThin : styles.element} /\u003e\n```\n\nHowever this becomes very repetitive rather quickly. To help, redux-responsive\nprovides a higher-order component for managing these styles. The `StyleSheet`\nhigher-order component takes a function of two arguments, the current state of the\nresponsive reducer, and any props passed to the component. The follow is\nequivalent to the logic above:\n\n```jsx\nimport {StyleSheet} from 'redux-responsive/react'\n\nconst stylesheet = (browser, props) =\u003e ({\n    element: {\n        color: 'blue',\n        _lessThan_medium: {\n            color: 'black',\n        }\n    }\n})\n\nconst component = StyleSheet(stylesheet)(({styles}) =\u003e (\n    \u003cdiv style={styles.element} /\u003e\n))\n```\n\n\n## redux-immutable\nThis library supports using [redux-immutable](https://www.npmjs.com/package/redux-immutable) to make the **root** of your state an Immutable.js Map or Record.\n\nHowever, transforming the branch of state managed by `redux-responsive` into Immutable data is not supported, because the `redux-responsive` reducer expects vanilla JS. Please keep this in mind if you're using SSR and transforming your state before hydrating.\n\n## FAQ\n\nredux-responsive is returning the wrong breakpoint on mobile devices, what may be causing this?\n\u003e This may be caused by not having the `\u003cmeta name=\"viewport\"\u003e` tag in the head of your HTML. Adding this code may resolve your issue: `\u003cmeta name=\"viewport\" content=\"width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0\"\u003e`. Read more [here](https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag) on how it works.\n\n## Versioning\n\n[Semver](http://semver.org/) is followed as closely as possible. For updates and migration instructions, see the [changelog](https://github.com/AlecAivazis/redux-responsive/wiki/Changelog).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlecAivazis%2Fredux-responsive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAlecAivazis%2Fredux-responsive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlecAivazis%2Fredux-responsive/lists"}