├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── FUNDING.yml ├── .gitignore ├── .mocharc.json ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dist ├── cjs │ ├── index.js │ └── index.js.map ├── esm │ ├── index.js │ └── index.js.map └── types │ ├── Component.d.ts │ ├── Context.d.ts │ ├── index.d.ts │ ├── mediaQuery.d.ts │ ├── toQuery.d.ts │ ├── types.d.ts │ └── useMediaQuery.d.ts ├── package.json ├── rollup.config.ts ├── src ├── Component.ts ├── Context.ts ├── index.ts ├── mediaQuery.ts ├── toQuery.ts ├── types.ts └── useMediaQuery.ts ├── test ├── Component_test.tsx ├── mediaQuery_test.ts ├── setup.js ├── toQuery_test.ts └── useMediaQuery_test.tsx └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | docs 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | rules: { 4 | 'no-extra-parens': 0, 5 | '@typescript-eslint/no-extra-parens': 1, 6 | quotes: [1, 'single'] 7 | }, 8 | extends: ['plugin:@typescript-eslint/recommended'] 9 | } 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: contra 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | build 5 | *.node 6 | coverage 7 | *.orig 8 | .idea 9 | yarn.lock 10 | package-lock.json 11 | docs 12 | dist/*.ts 13 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/mocharc.json", 3 | "require": "tsx" 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | docs 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | 3 | ### v10.0.1 4 | 5 | - Expose a MediaQuery named export, instead of just having it as the default export 6 | - Updates dev dependencies 7 | 8 | ### v10.0.0 9 | 10 | - Package with rollup 11 | - Adds ESM support 12 | - Removes UMD support - CJS support still exists 13 | - Still need UMD support? Stick with v9.0.1 14 | - Upgraded all dependencies to latest 15 | - Bumps minimum node version to 14 (dependencies are all at 14 anyways) 16 | 17 | ### v9.0.1 18 | 19 | - Export MediaQueryMatchers type 20 | 21 | ### v9.0.0 22 | 23 | - Convert project to typescript 24 | - Upgrade to Webpack 5 25 | - Fix a memory leak with useMediaQuery hook 26 | - Add support for React 18 27 | - Add docs webpage 28 | 29 | ### v9.0.0-beta 30 | 31 | #### Beta 9 32 | 33 | - Fix typescript output 34 | 35 | #### Beta 8 36 | 37 | - Fix webpack bundle issue (self is not defined) 38 | 39 | #### Beta 7 40 | 41 | - Improve typescript typings 42 | - Add typing support for React 18 43 | - Example setup with React 18 44 | - Upgrade to Webpack 5 45 | - Fix a bug with React 18 not re-rendering 46 | 47 | #### Beta 6 48 | 49 | - Convert project to typescript 50 | - Remove UMD build from project 51 | - Remove samples from project 52 | - Add docs webpage 53 | - Fix a memory leak with useMediaQuery hook 54 | 55 | ### v8.2.0 56 | 57 | - Repo maintenance - update dev dependencies and linter. Source maps updated. 58 | 59 | ### v8.1.0 60 | 61 | - Expose `toQuery` function for turning our MediaQuery objects into strings 62 | 63 | ### v8.0.3 64 | 65 | - Republish of 8.0.1 - version 8.0.2 was causing issues for people and is deprecated. 66 | 67 | ### v8.0.1 68 | 69 | - Include `src` folder in package for webpack to get proper sourcemaps 70 | 71 | ### v8.0.0 72 | 73 | - Adds Hooks API to make everything way easier (Thanks to @Tomekmularczyk) 74 | - See README for more info on how to use it 75 | - [BREAKING] Change `values` prop to `device` 76 | 77 | ### v7.0.0 78 | 79 | - Adds Context support to make testing and server-side rendering much easier (Thanks to @Tomekmularczyk) 80 | - See README for more info on how to use it 81 | - [BREAKING] Require React v16.8.0 or higher 82 | 83 | ### v6.1.2 84 | 85 | - Fix issue with values prop changing (https://github.com/contra/react-responsive/issues/197) 86 | 87 | ### v6.1.1 88 | 89 | - Fix `forceStatic` issue with previous release 90 | 91 | ### v6.1.0 92 | 93 | - Fix `values` prop existence not forcing static query resolution (Thanks @Herdismaria) 94 | 95 | ### v6.0.0 96 | 97 | - Require React v16.3.0 or higher 98 | - Update and simplify all lifecycle functions 99 | - Remove all support for wrapper elements 100 | - Previously, when either `component` or non-mediaquery props were provided, we would render a wrapper element. This behavior no longer exists. 101 | - The rendering logic is now a basic ternary - `matches ? props.children : null` 102 | - Thanks to @whatknight for doing the work here. 103 | 104 | ### v5.0.0 105 | 106 | - New changelog system, all manual now since the automated system was overcomplicated and missed things. 107 | - Breaking: `values` property always takes precedence when provided, no matter what (https://github.com/contra/react-responsive/issues/161) 108 | 109 | ### v4.0.5 (2018/03/07 17:11 +00:00) 110 | 111 | - [e61ec94](https://github.com/contra/react-responsive/commit/e61ec9482ae6ff17d3b73fab1fbcc445aab9e9aa) 4.0.5 (@contra) 112 | - [a8ec8e6](https://github.com/contra/react-responsive/commit/a8ec8e63221fccaef245366cd707aae3c19cb233) pre-release (@contra) 113 | - [#146](https://github.com/contra/react-responsive/pull/146) Guard against null children (@lunaleaps) 114 | - [3b40652](https://github.com/contra/react-responsive/commit/3b40652acbca56dc644e1b36fa50cabdb106ccdb) Guard against null children (@lunaleaps) 115 | 116 | ### v4.0.4 (2018/02/09 22:43 +00:00) 117 | 118 | - [77d37de](https://github.com/contra/react-responsive/commit/77d37de00c8ebeffe478ab0ef748d10b7862975a) 4.0.4 (@contra) 119 | - [19191ed](https://github.com/contra/react-responsive/commit/19191ed2522460109bfff19f22d6b879925599d3) closes #140 (@contra) 120 | 121 | ### v4.0.3 (2017/11/22 03:26 +00:00) 122 | 123 | - [191d938](https://github.com/contra/react-responsive/commit/191d9380d288b8de3dd6eeafd903db46b73d820a) 4.0.3 (@contra) 124 | - [a035cc5](https://github.com/contra/react-responsive/commit/a035cc5be9e01dc42bffacb0d8b68c916a8d0637) closes #134 (@contra) 125 | 126 | ### v4.0.2 (2017/11/21 19:55 +00:00) 127 | 128 | - [6e1a359](https://github.com/contra/react-responsive/commit/6e1a359f4f0c55db6823d321c86c10a2b76052a3) 4.0.2 (@contra) 129 | 130 | ### v4.0.1 (2017/11/21 15:54 +00:00) 131 | 132 | - [2aa6b94](https://github.com/contra/react-responsive/commit/2aa6b94dfb6104d111f38c07eb99b13b9616ac37) 4.0.1 (@contra) 133 | - [6908332](https://github.com/contra/react-responsive/commit/6908332218359646416d568c75bf6ef7e974b82f) maybe fixes #134 (@contra) 134 | 135 | ### v4.0.0 (2017/11/16 00:14 +00:00) 136 | 137 | - [b78938d](https://github.com/contra/react-responsive/commit/b78938d9f51dea13cc14e3227fce3694cde9dc63) 4.0.0 (@contra) 138 | - [bd1c554](https://github.com/contra/react-responsive/commit/bd1c5547a30483367479803799f264d160d86a1e) deps (@contra) 139 | - [#132](https://github.com/contra/react-responsive/pull/132) fix: support React 16 fragments (@jessepinho) 140 | - [aefef55](https://github.com/contra/react-responsive/commit/aefef5574caa63ee9387f0db1e88889d2e98a8f7) chore: build assets (@jessepinho) 141 | - [184b88e](https://github.com/contra/react-responsive/commit/184b88e069d8ab5f4f7f41d027ce37a0b8920e2c) style: make wrapChildren one line (@jessepinho) 142 | - [95edc69](https://github.com/contra/react-responsive/commit/95edc69daeffad4a4f8575f544ebd235fada10b3) feat: don't wrap strings, either (@jessepinho) 143 | - [0436fb5](https://github.com/contra/react-responsive/commit/0436fb5fe5985d6ce9046d4baab68a83d1d77c20) chore: update React peer dependency (@jessepinho) 144 | - [36f8372](https://github.com/contra/react-responsive/commit/36f837207e84f512c41ad66ac1fa60964f77148d) fix: support React 16 fragments (@jessepinho) 145 | - [#130](https://github.com/contra/react-responsive/pull/130) docs(CHANGELOG): 3.0.0 (@evenchange4) 146 | - [aaceb45](https://github.com/contra/react-responsive/commit/aaceb4599e9a6c6701d067cbffc33b7f95188458) docs(CHANGELOG): 3.0.0 (@evenchange4) 147 | - [#129](https://github.com/contra/react-responsive/pull/129) Fix view duplication in common uses example (@sonaye) 148 | - [55a0f47](https://github.com/contra/react-responsive/commit/55a0f47b99c5a31755399240cfbe6d0033da8866) Fix view duplication in common uses example (@sonaye) 149 | - [edbd8e7](https://github.com/contra/react-responsive/commit/edbd8e7ce8d757ef902d0f59b3d29679ac9e2616) 3.0 (@contra) 150 | 151 | ### v3.0.0 (2017/10/18 00:14 +00:00) 152 | 153 | - [8a318fe](https://github.com/contra/react-responsive/commit/8a318fe9ddf363b6a4e917a13d99b1098670175a) 3.0.0 (@contra) 154 | - [6bf6814](https://github.com/contra/react-responsive/commit/6bf681491e6ed260f89db85f6dad191a84cd523b) deps (@contra) 155 | - [#128](https://github.com/contra/react-responsive/pull/128) Fix linter and tests (@refinery29) 156 | - [71b611e](https://github.com/contra/react-responsive/commit/71b611eeabc52b271ebd3127223dd7b4b7824df2) Fix repo attribution (@cribbles) 157 | - [4e8186a](https://github.com/contra/react-responsive/commit/4e8186aeaae10c8ffe757e101e4e029ef8e9da4b) Fix test (import as default) (@cribbles) 158 | - [ce0475b](https://github.com/contra/react-responsive/commit/ce0475bb4badf816aec2d73649b2c00fb63a932d) Lint (remove semicolon) (@cribbles) 159 | - [73e2862](https://github.com/contra/react-responsive/commit/73e2862c3c30c4164527aa5afa7e441ecdbe3d91) Add toQuery to default exports (@cribbles) 160 | - [#124](https://github.com/contra/react-responsive/pull/124) Export toQuery function directly since src folder is no longer exported (@morganmccrory) 161 | - [f5d9b8b](https://github.com/contra/react-responsive/commit/f5d9b8b36270c9db7f8dfecf643eb09e5e3a6f30) Export toQuery function directly since src folder is no longer exported (@morganmccrory) 162 | - [fb23be4](https://github.com/contra/react-responsive/commit/fb23be4358654eb6c614b9766fbcbd4a5daac304) log (@contra) 163 | 164 | ### v2.0.0 (2017/10/02 21:11 +00:00) 165 | 166 | - [dee5570](https://github.com/contra/react-responsive/commit/dee55700f31e4fc54ab3faf1e974c3ee3c7abf21) 2.0.0 (@contra) 167 | - [24ff262](https://github.com/contra/react-responsive/commit/24ff2623d0a9d4a864c1635190d7fa1da37f3eb0) stupid lock file (@contra) 168 | - [cb120f8](https://github.com/contra/react-responsive/commit/cb120f8953c2e05aeac03530918a31d86d3be828) house clean (@contra) 169 | - [1e05b0c](https://github.com/contra/react-responsive/commit/1e05b0c8ad22f2008cb83506146bea88014066b3) housekeeping (@contra) 170 | - [14004ab](https://github.com/contra/react-responsive/commit/14004aba9ec54fd7b807abf6ed960a6cff87ada3) housekeeping (@contra) 171 | - [8ff7425](https://github.com/contra/react-responsive/commit/8ff742533e3fdf8d113c945cbe9291c16b8b3278) rebuild (@contra) 172 | - [#120](https://github.com/contra/react-responsive/pull/120) support react 16. (@whatknight) 173 | - [6b300d7](https://github.com/contra/react-responsive/commit/6b300d7cc1bb340afa14eb046290a5d03064120b) Merge branch 'master' into react16 (@contra) 174 | - [#119](https://github.com/contra/react-responsive/pull/119) Housekeeping (@whatknight) 175 | - [#121](https://github.com/contra/react-responsive/pull/121) Update README.txt (@vpicone) 176 | - [1d19af9](https://github.com/contra/react-responsive/commit/1d19af9a5e6ddf5baf11eb27f455165430cdd405) Update README.txt (@vpicone) 177 | - [bb69da2](https://github.com/contra/react-responsive/commit/bb69da2e74b615132dd30c4eecad2b88080e5287) support react 16. (@whatknight) 178 | - [e78cab9](https://github.com/contra/react-responsive/commit/e78cab9fafceb30195ffca6858df35560f3675b6) bump sinon (@whatknight) 179 | - [8380bd9](https://github.com/contra/react-responsive/commit/8380bd91c3fbb73957f7410bd2512266f7c7053a) update webpack (@whatknight) 180 | - [9429386](https://github.com/contra/react-responsive/commit/9429386ce0891c1904baadf725837e0a3f282746) remove transform runtime (@whatknight) 181 | - [48b2a33](https://github.com/contra/react-responsive/commit/48b2a33ad9a6c61fbbbac6dad6f54838f949d61a) update test deps (@whatknight) 182 | - [81d9d18](https://github.com/contra/react-responsive/commit/81d9d181d08634527b49bb62c66f5b2c72e606f1) update eslint (@whatknight) 183 | - [307b31d](https://github.com/contra/react-responsive/commit/307b31d16cf4d336f50ad4c53739166e4bd1d80e) update babel config (@whatknight) 184 | - [#117](https://github.com/contra/react-responsive/pull/117) Update README.md (@modosc) 185 | - [0b4d019](https://github.com/contra/react-responsive/commit/0b4d0197d1011c3e5899cc6003556a6526350bc1) Update README.md (@modosc) 186 | - [c55a77e](https://github.com/contra/react-responsive/commit/c55a77ec86c2db7d2aae4a3e67a08dba09a3a455) closes #116 (@contra) 187 | 188 | ### v1.3.4 (2017/07/14 21:36 +00:00) 189 | 190 | - [c93ac7a](https://github.com/contra/react-responsive/commit/c93ac7a696a8d3f0f8a5b71ce7836375de13a28d) 1.3.4 (@contra) 191 | - [#109](https://github.com/contra/react-responsive/pull/109) fix Cannot read property 'removeListener' of undefined (@modosc) 192 | - [ea3d577](https://github.com/contra/react-responsive/commit/ea3d5774bc59fa06ad50d60e582a9f6da94b9bd5) update matchmediaquery@^0.2.1 193 | - [3687115](https://github.com/contra/react-responsive/commit/3687115de3603cfb32eb5f3e4d3b9883b12f3ed3) fix Cannot read property 'removeListener' of undefined 194 | - [83c2409](https://github.com/contra/react-responsive/commit/83c24092225fa20c96c4bded03e845cb4baf5bec) 1.3.3 (@contra) 195 | 196 | ### v1.3.3 (2017/07/14 19:32 +00:00) 197 | 198 | - [58fdb89](https://github.com/contra/react-responsive/commit/58fdb89893de8a9ec5495624c19f54bcced220c3) 1.3.3 (@contra) 199 | - [e3781d2](https://github.com/contra/react-responsive/commit/e3781d208f7d7528add58b9b69fb998fe8f1e94e) diff (@contra) 200 | - [#108](https://github.com/contra/react-responsive/pull/108) fix PropTypes warnings, fix react-addons-test-utils deprecation, fix … (@modosc) 201 | - [49919d0](https://github.com/contra/react-responsive/commit/49919d09b7a4e984873b570132ada5554300f071) fix PropTypes warnings, fix react-addons-test-utils deprecation, fix eslint to work with test/ 202 | - [9957f24](https://github.com/contra/react-responsive/commit/9957f2445bd4d16b8d2ae1ae8a335dc2c95d476f) 1.3.2 (@contra) 203 | 204 | ### v1.3.2 (2017/07/14 14:47 +00:00) 205 | 206 | - [a595593](https://github.com/contra/react-responsive/commit/a595593f413b5012de57f8ca7eabec5490a7426c) 1.3.2 (@contra) 207 | - [516f270](https://github.com/contra/react-responsive/commit/516f2703dd4f35611d60a399ebd61273140ff88e) merge (@contra) 208 | - [40fe34e](https://github.com/contra/react-responsive/commit/40fe34e003669f44692aedb054079126bedc5bc4) build (@contra) 209 | - [85ae4cd](https://github.com/contra/react-responsive/commit/85ae4cd10fb0ea98a313f4335590452893f329ad) build (@contra) 210 | - [69cde10](https://github.com/contra/react-responsive/commit/69cde106d817b3f4df7cbdd540ed27edfcc7f2d8) changes (@contra) 211 | - [#106](https://github.com/contra/react-responsive/pull/106) Fixing a memory leak. (@ncochard) 212 | - [00af280](https://github.com/contra/react-responsive/commit/00af280f57d979963d6c1d445e9c8c088ee03b82) 1.3.1 213 | - [1e8dcb9](https://github.com/contra/react-responsive/commit/1e8dcb9b790c4ce8cf24b96936f4d5760c3a3494) Created 'matchmediaquery' as a replacement for 'matchmedia'. 214 | - [6df7684](https://github.com/contra/react-responsive/commit/6df7684736797589245a643eb72e8e243dd987be) Fix for a memory leak. 215 | - [#102](https://github.com/contra/react-responsive/pull/102) Close #74 (@WRONGWAY4YOU) 216 | - [7877b80](https://github.com/contra/react-responsive/commit/7877b8003dc8f69f6350304c319daa3e35351aae) Merge branch 'master' into iss74 (@wrongway4you) 217 | - [374b290](https://github.com/contra/react-responsive/commit/374b290722e50c7bade5e136a08ad53671b7df4a) Close #74 (@wrongway4you) 218 | - [4156475](https://github.com/contra/react-responsive/commit/4156475e882fa3f8098991a6b713dd743b24a739) Merge branch 'remove-npm-bin-prefixes' (@wrongway4you) 219 | - [#101](https://github.com/contra/react-responsive/pull/101) Remove unneeded "$(npm bin)/" prefixes (@WRONGWAY4YOU) 220 | - [adfaf69](https://github.com/contra/react-responsive/commit/adfaf69be82b56f91c66da7b17735aa4faa901f5) Remove unneeded "$(npm bin)/" prefixes in `package.json` (@wrongway4you) 221 | - [#100](https://github.com/contra/react-responsive/pull/100) Document common use cases (@sonaye) 222 | - [b917322](https://github.com/contra/react-responsive/commit/b917322352dbb68939f2e1f140a89e4836dad4d4) Document common use cases (@sonaye) 223 | 224 | ### v1.3.0 (2017/05/09 01:13 +00:00) 225 | 226 | - [44a7bf8](https://github.com/contra/react-responsive/commit/44a7bf8bd6f4289c4a644d4d1cdefbc5b01523c2) 1.3.0 (@contra) 227 | - [#97](https://github.com/contra/react-responsive/pull/97) Add onChange and onChangeBefore callbacks (@hiddenboox) 228 | - [6cb52b1](https://github.com/contra/react-responsive/commit/6cb52b15a452f1189bd4f89a4c5ce41bee21fe02) Add onBeforeChange callback (@hiddenboox) 229 | - [#96](https://github.com/contra/react-responsive/pull/96) Adding demo link to readme (@scottwarren) 230 | - [5836dfe](https://github.com/contra/react-responsive/commit/5836dfee1e85675ba1ea796a0e3cc75bba77d580) Adding link to readme (@scottwarren) 231 | - [2c6b00d](https://github.com/contra/react-responsive/commit/2c6b00da63091b3bada0b8fe6105e50e29b37fde) Add onChange callback (@d4rky-pl) 232 | 233 | ### v1.2.10 (2017/04/19 18:32 +00:00) 234 | 235 | - [ab7ff5c](https://github.com/contra/react-responsive/commit/ab7ff5cddcba1f22f3ace26dd777bbc530449c6c) 1.2.10 (@contra) 236 | - [4f8f916](https://github.com/contra/react-responsive/commit/4f8f91612d389b06cfed42723b3fe0b8e1a66f8c) build (@contra) 237 | 238 | ### v1.2.9 (2017/04/19 03:27 +00:00) 239 | 240 | - [07849af](https://github.com/contra/react-responsive/commit/07849afe0f7aee32c4db3dd4323bb93dbf23f1d1) 1.2.9 (@contra) 241 | - [#90](https://github.com/contra/react-responsive/pull/90) Replace proptypes in index file (@rmdort) 242 | - [a4a9120](https://github.com/contra/react-responsive/commit/a4a912073dff91401196fd8bc0ff41253573c49d) Removed React proptype in index (@rmdort) 243 | - [c1a8081](https://github.com/contra/react-responsive/commit/c1a8081809d91542f7ce77e13bec42cbc26cd067) Spellfix (@rmdort) 244 | - [0d7ffb6](https://github.com/contra/react-responsive/commit/0d7ffb6cf27027d7f1e64157a4d1b7ba45ffb0b1) Replace react proptypes (@rmdort) 245 | - [48c303f](https://github.com/contra/react-responsive/commit/48c303f7c47e1d553f4e4d5d639073e72f7822e7) build (@contra) 246 | 247 | ### v1.2.8 (2017/04/18 18:45 +00:00) 248 | 249 | - [4778c1e](https://github.com/contra/react-responsive/commit/4778c1e3793bffe15fd66cc0f7c8cb30c7cc59f0) 1.2.8 (@contra) 250 | - [#88](https://github.com/contra/react-responsive/pull/88) Added prop-type library for React 16 (@rmdort) 251 | - [7039ef6](https://github.com/contra/react-responsive/commit/7039ef6e30e409d83a8cf5e3a894cfdb136d3582) Added prop-type library for react 16 (@rmdort) 252 | - [#86](https://github.com/contra/react-responsive/pull/86) docs(readme): add install part (@kud) 253 | - [4665934](https://github.com/contra/react-responsive/commit/4665934ac824bca12c009a98f40c71bc8f47a081) docs(readme): add install part (@kud) 254 | - [8c53639](https://github.com/contra/react-responsive/commit/8c536392459673d46e0df3682adf42c8196b17e0) changelog (@contra) 255 | 256 | ### v1.2.7 (2017/03/14 16:17 +00:00) 257 | 258 | - [ee6b142](https://github.com/contra/react-responsive/commit/ee6b1429caa0fefd6e6f7c9313fe54fe13a61d82) 1.2.7 (@contra) 259 | - [#83](https://github.com/contra/react-responsive/pull/83) Add react 0.14.x as potential peer dep (@jesstelford) 260 | - [29266f0](https://github.com/contra/react-responsive/commit/29266f0db6598feaebcd4dc9dc907cdda3bafc2f) Peer dep on all react versions (@jesstelford) 261 | - [1007031](https://github.com/contra/react-responsive/commit/100703116f9649cf1e0e125a10c9384a9366ecd3) Add react 0.14.x as potential peer dep (@jesstelford) 262 | - [bc69299](https://github.com/contra/react-responsive/commit/bc692990d149f8a08bad4a12df78691816f75e81) changes (@contra) 263 | 264 | ### v1.2.6 (2017/01/19 03:06 +00:00) 265 | 266 | - [342621d](https://github.com/contra/react-responsive/commit/342621d0407ae2e590a33c280ec2ca39d8bf3e8e) 1.2.6 (@contra) 267 | - [#78](https://github.com/contra/react-responsive/pull/78) Removed babel-polyfill (@vjancik) 268 | - [ebfeec0](https://github.com/contra/react-responsive/commit/ebfeec0473b2ca8040dbe13e698fa08366a7808b) Removed babel-polyfill (@vjancik) 269 | - [9b91c9c](https://github.com/contra/react-responsive/commit/9b91c9cc341693a8282199edaed04a9c4b7a7f7a) #66 (@contra) 270 | - [56b794a](https://github.com/contra/react-responsive/commit/56b794a0fcac2a28c7bd87e7263ad2b93c52e348) dist fix (@contra) 271 | 272 | ### v1.2.4 (2016/11/24 00:51 +00:00) 273 | 274 | - [5e8ae33](https://github.com/contra/react-responsive/commit/5e8ae33d53e390193cf289b9f2ce7a6f95c47d90) 1.2.4 (@contra) 275 | - [708e250](https://github.com/contra/react-responsive/commit/708e25030963d967db7dcfa5b21f95bcd84832ed) only publish dist (@contra) 276 | - [2b36c54](https://github.com/contra/react-responsive/commit/2b36c54847f084e1830a1bcd1c885b6ab5102185) changelog (@contra) 277 | 278 | ### v1.2.3 (2016/11/23 00:26 +00:00) 279 | 280 | - [7d95ef1](https://github.com/contra/react-responsive/commit/7d95ef1d83e6acba43ac15aedda03eecdf66f6bf) 1.2.3 (@contra) 281 | - [404924f](https://github.com/contra/react-responsive/commit/404924ff302378a30dbb4f438b237c955b5528b0) changelog fix (@contra) 282 | 283 | ### v1.2.2 (2016/11/22 23:57 +00:00) 284 | 285 | - [d337ce3](https://github.com/contra/react-responsive/commit/d337ce35a00bbb383a656d7453e8b28a2b30ea87) 1.2.2 (@contra) 286 | - [#70](https://github.com/contra/react-responsive/pull/70) Wrap children if it's single-element array (@Instamotor-Labs) 287 | - [341fdec](https://github.com/contra/react-responsive/commit/341fdecc2c3f7f56529dc9941768690457a27c30) Wrap children if it's single-element array (@skydan) 288 | - [#65](https://github.com/contra/react-responsive/pull/65) Use ES2015 (@whatknight) 289 | - [1b07af3](https://github.com/contra/react-responsive/commit/1b07af3957c18a9aeaefdc7b989b75f29fb0ac5e) Clean up webpack file syntax. (@whatknight) 290 | - [b72e2c0](https://github.com/contra/react-responsive/commit/b72e2c032f24eefd33580774ec370ac50d720bf2) Update source to ES2015 syntax. (@whatknight) 291 | - [94dbf57](https://github.com/contra/react-responsive/commit/94dbf5717b0b4d4bc9ab2b934c0c1a1585c37b68) add gitbook stuff. (@whatknight) 292 | - [6aa759d](https://github.com/contra/react-responsive/commit/6aa759d66ee7571c4ad8f3265a29dc121635ec80) autofix lint errors (@whatknight) 293 | - [902d432](https://github.com/contra/react-responsive/commit/902d432d96ae7c9ff8012598a7fa75a89b3f8fd8) Use eslint instead of jshint. (@whatknight) 294 | - [9b22166](https://github.com/contra/react-responsive/commit/9b22166668118b077a8a3e1025a09f5909d0a985) Remove gulp. (@whatknight) 295 | - [2810466](https://github.com/contra/react-responsive/commit/28104666aafcf5d7a2babebe154cc52d10e186b7) Build using babel and webpack. (@whatknight) 296 | - [43b5741](https://github.com/contra/react-responsive/commit/43b5741e79aa0e79fe04174c30d442c2b0ca5706) update author, license, and urls. (@whatknight) 297 | - [477174c](https://github.com/contra/react-responsive/commit/477174c4e6738f9db913ee6f18924365bd9de019) 1.2.1 (@contra) 298 | - [f185c21](https://github.com/contra/react-responsive/commit/f185c21d763f05a11c9e4768ada0acda722b9f9e) rebuild (@contra) 299 | - [7895cde](https://github.com/contra/react-responsive/commit/7895cdee5fe3262c70aff29ab679044d9470f2b1) 1.2.0 (@contra) 300 | - [#64](https://github.com/contra/react-responsive/pull/64) Pass function as children. (@whatknight) 301 | - [5ce699b](https://github.com/contra/react-responsive/commit/5ce699bc02929f48da4a2cc32568d6905a0db346) Add space to readme. (@whatknight) 302 | - [b27da86](https://github.com/contra/react-responsive/commit/b27da8658410a4a069858f9d1cd0c684f9a2df96) Update docs with new feature. (@whatknight) 303 | - [80fddd9](https://github.com/contra/react-responsive/commit/80fddd971f9239ba885fa9892a80cd5081f7e401) Add ability to render with a function as the component child. (@whatknight) 304 | 305 | ### v1.1.5 (2016/09/15 04:13 +00:00) 306 | 307 | - [b6364b6](https://github.com/contra/react-responsive/commit/b6364b6157a9fca972cdb9170bed494b805948da) 1.1.5 (@contra) 308 | - [#62](https://github.com/contra/react-responsive/pull/62) Fix for Uncaught Invariant Violation #56. (@rhavill) 309 | - [3199bef](https://github.com/contra/react-responsive/commit/3199bef42f9d715701e276740bb997c2b474b51c) Added unit test to make sure MediaQuery render function can handle an empty array as children. 310 | - [fdb34e8](https://github.com/contra/react-responsive/commit/fdb34e8d830bc6d405b07afceca1dfb117808591) Fix for Uncaught Invariant Violation #56. Return null from render function when MediaQuery has no children. 311 | - [#61](https://github.com/contra/react-responsive/pull/61) Small fixes to indentation in README (@TSMMark) 312 | - [b75d5d8](https://github.com/contra/react-responsive/commit/b75d5d8a89604c294f8bf43f6ec74974421eae31) small fixes to indentation in README (@TSMMark) 313 | - [eac6444](https://github.com/contra/react-responsive/commit/eac6444d4059268b4744679edd209e484d222758) 1.1.4 (@contra) 314 | - [#58](https://github.com/contra/react-responsive/pull/58) Doesn't throw error with empty children (@nkov) 315 | - [df33365](https://github.com/contra/react-responsive/commit/df333655bdb658d2046289ac19e7153594cb8cd9) doesnt throw error with empty children (@nkov) 316 | 317 | ### v1.1.3 (2016/04/25 17:03 +00:00) 318 | 319 | - [ba1cb39](https://github.com/contra/react-responsive/commit/ba1cb39afa5259bda12ffe0dd7c57919c535397e) 1.1.3 (@contra) 320 | - [#53](https://github.com/contra/react-responsive/pull/53) Always wrap children if it's a string. (@whatknight) 321 | - [23f35cb](https://github.com/contra/react-responsive/commit/23f35cb2f4b9c906c267854077cb00f298c89e41) Always wrap children if it's a string. (@whatknight) 322 | - [02dd120](https://github.com/contra/react-responsive/commit/02dd120d5f22acc744edca34723a0ec9131dc8d3) add dev dep for react (@contra) 323 | - [#52](https://github.com/contra/react-responsive/pull/52) Add testing with jsdom. (@whatknight) 324 | - [#51](https://github.com/contra/react-responsive/pull/51) Fix bug where string would cause invariant error. (@whatknight) 325 | - [55707f1](https://github.com/contra/react-responsive/commit/55707f103ac9d53441943e80b96f0ece1b189ba5) Add testing with jsdom. (@whatknight) 326 | - [7dacb42](https://github.com/contra/react-responsive/commit/7dacb423884bc00aefc5579674f498af0eede79a) Fix bug where string would cause invariant error. (@whatknight) 327 | 328 | ### v1.1.2 (2016/04/08 05:07 +00:00) 329 | 330 | - [8d03ff0](https://github.com/contra/react-responsive/commit/8d03ff03e69d2c967a4c0c522523d0dc7af7c960) 1.1.2 (@contra) 331 | - [#47](https://github.com/contra/react-responsive/pull/47) Bump react peer dependency to allow v15 (@cesarandreu) 332 | - [87619fe](https://github.com/contra/react-responsive/commit/87619fe29ab529cd9e6b4e8a1a22dfc820b11a34) Bump react peer dependency to allow v15 (@cesarandreu) 333 | - [#46](https://github.com/contra/react-responsive/pull/46) Use API to examine number of children (@jdlehman) 334 | - [6c75c26](https://github.com/contra/react-responsive/commit/6c75c26970b2f4f58bdf947e379f487bcf660845) Use API to examine number of children (@jdlehman) 335 | - [c82c671](https://github.com/contra/react-responsive/commit/c82c67172b9dbd17dcdc6dbd918cb1568423661c) 1.1.1 (@contra) 336 | - [#39](https://github.com/contra/react-responsive/pull/39) fixed usage of Object.assign for older browsers (@pekeler) 337 | - [ce639c9](https://github.com/contra/react-responsive/commit/ce639c9b61944add8a9a51e2b0accbdf89e7dea2) fixed usage of Object.assign for older browsers (@pekeler) 338 | 339 | ### v1.1.0 (2016/01/10 23:53 +00:00) 340 | 341 | - [cfff39a](https://github.com/contra/react-responsive/commit/cfff39a73e1f8ffa48d8a74ac2a1d5816255e4ca) 1.1.0 (@contra) 342 | - [#37](https://github.com/contra/react-responsive/pull/37) Without lodash (@pekeler) 343 | - [ea47d21](https://github.com/contra/react-responsive/commit/ea47d21e49b91693c3aaecbc7af2706459dbfa0e) reverted spelling, dist (@pekeler) 344 | - [26e540d](https://github.com/contra/react-responsive/commit/26e540d8e718d842e363807e8fef1d07b6eda6c3) reverted spelling (@pekeler) 345 | - [1cd1947](https://github.com/contra/react-responsive/commit/1cd1947d71693b40e6afa784a93a5a52199d4128) readded old react versions (@pekeler) 346 | - [#38](https://github.com/contra/react-responsive/pull/38) reverted stupid spelling (@pekeler) 347 | - [996f15e](https://github.com/contra/react-responsive/commit/996f15eca95e735de7cdeb7c04c9eb3e8615f710) reverted stupid spelling (@pekeler) 348 | - [#36](https://github.com/contra/react-responsive/pull/36) Newer dependencies (@pekeler) 349 | - [83f461b](https://github.com/contra/react-responsive/commit/83f461bc6e2e7bdcd79c90e0c8064d850b452a1d) replaced lodash.omit with own implementation to reduce total file size (@pekeler) 350 | - [4534c1f](https://github.com/contra/react-responsive/commit/4534c1fe9666987a864e1d769635407d120fcb33) make npm test work (@pekeler) 351 | - [5cd9036](https://github.com/contra/react-responsive/commit/5cd9036e65c0f1b48c2268b73158e627013aade1) updated to latest dependecies, sample can't be easily backwards compatible with old react versions so we focus on the latest react (@pekeler) 352 | - [29d0d08](https://github.com/contra/react-responsive/commit/29d0d081a352f8723507245ea9f223a144895650) more readme info (@contra) 353 | 354 | ### v1.0.1 (2015/11/13 20:28 +00:00) 355 | 356 | - [b4ce24e](https://github.com/contra/react-responsive/commit/b4ce24ea423c062dfd4bace514b5614e119eb96c) 1.0.1 (@contra) 357 | - [#32](https://github.com/contra/react-responsive/pull/32) Add children to excluded keys (@ch2ch3) 358 | - [30b3f9f](https://github.com/contra/react-responsive/commit/30b3f9f8278bad15aad8b3fa8f393138c99b5610) Add children to excluded keys (@ch2ch3) 359 | 360 | ### v1.0.0 (2015/11/09 23:48 +00:00) 361 | 362 | - [a20d7f3](https://github.com/contra/react-responsive/commit/a20d7f3f43758400cb266a8aadab7b4dede843e6) 1.0.0 (@contra) 363 | - [#30](https://github.com/contra/react-responsive/pull/30) Do not require a component to wrap MediaQuery children (@jdlehman) 364 | - [27c09e5](https://github.com/contra/react-responsive/commit/27c09e565d98f902d798ffd2f7348f1b5d71d54f) Pass props to single child even if not using a wrapper (@jdlehman) 365 | - [56ecbb2](https://github.com/contra/react-responsive/commit/56ecbb26d014464b00fc1c0893b74a4e18b65225) Add documentation for component prop (@jdlehman) 366 | - [4543f17](https://github.com/contra/react-responsive/commit/4543f172a744402c5dd4a686acfad49baf9ee47a) Do not require a component to wrap MediaQuery children (@jdlehman) 367 | 368 | ### v0.0.10 (2015/10/08 22:50 +00:00) 369 | 370 | - [62af1d5](https://github.com/contra/react-responsive/commit/62af1d564767e7997af628b4764e330b8d29dae1) 0.0.10 (@contra) 371 | - [#28](https://github.com/contra/react-responsive/pull/28) support react 0.14 (@0x80) 372 | - [d1dc1e9](https://github.com/contra/react-responsive/commit/d1dc1e9d0a3c67427a368c9cc3f27f6493a4f2c6) support react 0.14 (@0x80) 373 | - [#26](https://github.com/contra/react-responsive/pull/26) Allow for react 0.14.0-rc1 as peer-dependency (@npasserini) 374 | - [b3e687d](https://github.com/contra/react-responsive/commit/b3e687d96fc6fbe28ddf421e6b7ca62e6434142e) Allow for react 0.14.0-rc1 as peer-dependency (@npasserini) 375 | 376 | ### v0.0.8 (2015/08/13 20:18 +00:00) 377 | 378 | - [969736a](https://github.com/contra/react-responsive/commit/969736a6c4583fbf41a30bfeb94b470f01af9a2f) 0.0.8 (@contra) 379 | - [#18](https://github.com/contra/react-responsive/pull/18) There is possible memory leak and bug leads to many listeners. It is … (@vavdav) 380 | - [d435265](https://github.com/contra/react-responsive/commit/d4352658b07718d7286b71603eb4fef790d8bf12) There is possible memory leak and bug leads to many listeners. It is necessary to remove listener before creating new object. (@vavdav) 381 | - [#17](https://github.com/contra/react-responsive/pull/17) Add support for React 0.14.0-beta1 (@frederickfogerty) 382 | - [45861f7](https://github.com/contra/react-responsive/commit/45861f7dea95a4b7486be7949b7e08bee24bed74) Update package.json (@frederickfogerty) 383 | - [84fb5a8](https://github.com/contra/react-responsive/commit/84fb5a849845c77ecdbd2a8c89524241a83f33b3) Add support for React 0.14.0-beta1 (@frederickfogerty) 384 | - [#16](https://github.com/contra/react-responsive/pull/16) Use `hyphenate-style-name` module instead of React internal (@rexxars) 385 | - [7046106](https://github.com/contra/react-responsive/commit/704610624d1ede95c0043e43a4be8fa6346315c3) Use `hyphenate-style-name` module instead of React internal (@rexxars) 386 | 387 | ### v0.0.7 (2015/07/22 06:22 +00:00) 388 | 389 | - [4050ab3](https://github.com/contra/react-responsive/commit/4050ab3041ea7075e2f59c5a5acd95451d648aaa) 0.0.7 (@contra) 390 | - [413b4e0](https://github.com/contra/react-responsive/commit/413b4e0c6b42b2f7ecf3d6a26566c4bc12d18c40) fix version range (@contra) 391 | - [e1dc870](https://github.com/contra/react-responsive/commit/e1dc870ef10573b063248b105788dcbe8c3953af) Update package.json (@contra) 392 | 393 | ### v0.0.6 (2015/03/30 17:12 +00:00) 394 | 395 | - [e9214ce](https://github.com/contra/react-responsive/commit/e9214cecfe74be5c5f9d8a7ba7d2e49c3221794c) 0.0.6 (@contra) 396 | - [7bcb9b5](https://github.com/contra/react-responsive/commit/7bcb9b5595f1a6b759b2231717b97cfe4a111630) fix linting (@contra) 397 | - [#12](https://github.com/contra/react-responsive/pull/12) Provide more flexible React peer dependency range (@colindresj) 398 | - [7db99e3](https://github.com/contra/react-responsive/commit/7db99e321db4f4c24b7da759bb4247658208ec2f) Provide more flexible React peer dependency range (@colindresj) 399 | - [#11](https://github.com/contra/react-responsive/pull/11) switch to matchmedia module for client/server abstraction (@phated) 400 | - [7d05ab6](https://github.com/contra/react-responsive/commit/7d05ab60e7feb53d118ea94f92e5103bdae08627) change names to MediaQuery and add `all` type (@phated) 401 | - [39ab71d](https://github.com/contra/react-responsive/commit/39ab71d2f60a238ae3ac78bc485f804f2e2fc07e) add type property (@phated) 402 | - [6c6054a](https://github.com/contra/react-responsive/commit/6c6054a48df4620c301f31ffe6e456f0a2000142) update readme (@phated) 403 | - [48e6c5d](https://github.com/contra/react-responsive/commit/48e6c5d6c3b8360603f2aeabb599b3c601cb74b9) update reactify, add example, use React.PropTypes.shape to define values (@phated) 404 | - [759154b](https://github.com/contra/react-responsive/commit/759154bfb098aed3be707086d0448ec39cc42ffe) switch to matchmedia module for client/server abstraction (@phated) 405 | 406 | ### v0.0.5 (2015/02/17 23:42 +00:00) 407 | 408 | - [72f4e25](https://github.com/contra/react-responsive/commit/72f4e250a311dfae0e2de95de6ae337a6a67cc9b) 0.0.5 (@contra) 409 | - [acef7f0](https://github.com/contra/react-responsive/commit/acef7f0a437cba4c23e72e9c4e6bbf4bb0c6ccf9) lint fixes (@contra) 410 | - [#10](https://github.com/contra/react-responsive/pull/10) fix usage of idiomatic react props (@ChrisSki) 411 | - [27e9f25](https://github.com/contra/react-responsive/commit/27e9f25191bc119a11d6d3a1c2ae3ed22deb1b89) remove propTypes: types (@ChrisSki) 412 | - [6f4b364](https://github.com/contra/react-responsive/commit/6f4b364718b3e031c24aff7dcd5f08419760f8a3) actually remove types this time (@ChrisSki) 413 | - [dcdea6a](https://github.com/contra/react-responsive/commit/dcdea6a30efe29fa3fbc2b6e90c3164e29dcf0e1) remove types prop (@ChrisSki) 414 | - [207a385](https://github.com/contra/react-responsive/commit/207a385ce17b2e21d22206d6dbfb9e09594c8c9b) fix usage of idiomatic react props (@ChrisSki) 415 | 416 | ### v0.0.4 (2015/02/09 09:15 +00:00) 417 | 418 | - [4f91d06](https://github.com/contra/react-responsive/commit/4f91d063f1d4b3f739e261aa54d090a5bb5cb402) 0.0.4 (@contra) 419 | - [609e3ae](https://github.com/contra/react-responsive/commit/609e3ae14dcbb8d0d99337c54ffbaba5514e77be) fix linter (@contra) 420 | - [#9](https://github.com/contra/react-responsive/pull/9) mergeInto removed and window checked (@fvitullo) 421 | - [c9eef42](https://github.com/contra/react-responsive/commit/c9eef42c24bf584e2bdc44150351d21d6db3ae1e) fixes #7 422 | - [26f2e95](https://github.com/contra/react-responsive/commit/26f2e95b811ae4b81bc7654723b9d8fc57fc8e50) mergeInto replaced with object-assign 423 | 424 | ### v0.0.3 (2015/02/04 01:12 +00:00) 425 | 426 | - [dcdb812](https://github.com/contra/react-responsive/commit/dcdb81278f20ede8e8e51fae07c8182ddd149cc3) 0.0.3 (@contra) 427 | - [33fbeff](https://github.com/contra/react-responsive/commit/33fbeff74c3388d14765f8ee5a1125a09b676d8a) closes #6 (@contra) 428 | 429 | ### v0.0.2 (2015/01/27 20:11 +00:00) 430 | 431 | - [085d2f3](https://github.com/contra/react-responsive/commit/085d2f353d0d0457359c199381f152796c5e155c) 0.0.2 (@contra) 432 | - [f5674b6](https://github.com/contra/react-responsive/commit/f5674b602f575f9c50e1a37f0cbdd6ede2216fec) react 0.12 (@contra) 433 | - [d6c4a4b](https://github.com/contra/react-responsive/commit/d6c4a4b29529a53e0a0c44358c23195dd0314f84) readme (@contra) 434 | - [d713f0b](https://github.com/contra/react-responsive/commit/d713f0bf4c19bb6cdc54ec23c9dcb1d5d57f8071) docs (@contra) 435 | - [af44f6c](https://github.com/contra/react-responsive/commit/af44f6c4914a62ca5fadbb770ab7c9b533eb294d) respond to prop changes (@contra) 436 | - [1413782](https://github.com/contra/react-responsive/commit/14137828b3ea46d6d6fad317cd80550142afa949) basically done (@contra) 437 | - [ec2ed95](https://github.com/contra/react-responsive/commit/ec2ed950203fa79d63a2b97cd24e2147d95df56c) start on js -> mq thing (@contra) 438 | - [c145e0b](https://github.com/contra/react-responsive/commit/c145e0b1c72c331033ce50da632cc3392914dd0f) fixdemo (@contra) 439 | - [ef59366](https://github.com/contra/react-responsive/commit/ef59366b7bc5482286978db358344875591ab200) demo (@contra) 440 | - [8c08276](https://github.com/contra/react-responsive/commit/8c08276194109441d5766e83fe5d455d0a5dc9c1) yo (@contra) 441 | - [891ed6e](https://github.com/contra/react-responsive/commit/891ed6ed57eb89c1de4b03d52519269f6cbac9e9) fix readme (@contra) 442 | - [dc3fb1b](https://github.com/contra/react-responsive/commit/dc3fb1bdaf6d5f4f080a59c7981f760f9cb5ef13) initial (@contra) 443 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Contra 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-responsive [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] 2 | 3 | ## Information 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Packagereact-responsive
DescriptionMedia queries in react for responsive design
Browser Version>= IE6*
Demo
21 | 22 | The best supported, easiest to use react media query module. 23 | 24 | ## Install 25 | 26 | ```console 27 | $ npm install react-responsive --save 28 | ``` 29 | 30 | ## Example Usage 31 | 32 | ### With Hooks 33 | 34 | Hooks is a new feature available in 8.0.0! 35 | 36 | ```jsx 37 | import React from 'react' 38 | import { useMediaQuery } from 'react-responsive' 39 | 40 | const Example = () => { 41 | const isDesktopOrLaptop = useMediaQuery({ 42 | query: '(min-width: 1224px)' 43 | }) 44 | const isBigScreen = useMediaQuery({ query: '(min-width: 1824px)' }) 45 | const isTabletOrMobile = useMediaQuery({ query: '(max-width: 1224px)' }) 46 | const isPortrait = useMediaQuery({ query: '(orientation: portrait)' }) 47 | const isRetina = useMediaQuery({ query: '(min-resolution: 2dppx)' }) 48 | 49 | return ( 50 |
51 |

Device Test!

52 | {isDesktopOrLaptop &&

You are a desktop or laptop

} 53 | {isBigScreen &&

You have a huge screen

} 54 | {isTabletOrMobile &&

You are a tablet or mobile phone

} 55 |

Your are in {isPortrait ? 'portrait' : 'landscape'} orientation

56 | {isRetina &&

You are retina

} 57 |
58 | ) 59 | } 60 | ``` 61 | 62 | ### With Components 63 | 64 | ```jsx 65 | import MediaQuery from 'react-responsive' 66 | 67 | const Example = () => ( 68 |
69 |

Device Test!

70 | 71 |

You are a desktop or laptop

72 | 73 |

You also have a huge screen

74 |
75 |
76 | 77 | {/* You can also use a function (render prop) as a child */} 78 | {(matches) => 79 | matches ?

You are retina

:

You are not retina

80 | } 81 |
82 |
83 | ) 84 | ``` 85 | 86 | ## API 87 | 88 | ### Using Properties 89 | 90 | To make things more idiomatic to react, you can use camel-cased shorthands to construct media queries. 91 | 92 | For a list of all possible shorthands and value types see https://github.com/yocontra/react-responsive/blob/master/src/mediaQuery.ts#L9. 93 | 94 | Any numbers given as shorthand will be expanded to px (`1234` will become `'1234px'`). 95 | 96 | The CSS media queries in the example above could be constructed like this: 97 | 98 | ```jsx 99 | import React from 'react' 100 | import { useMediaQuery } from 'react-responsive' 101 | 102 | const Example = () => { 103 | const isDesktopOrLaptop = useMediaQuery({ minWidth: 1224 }) 104 | const isBigScreen = useMediaQuery({ minWidth: 1824 }) 105 | const isTabletOrMobile = useMediaQuery({ maxWidth: 1224 }) 106 | const isPortrait = useMediaQuery({ orientation: 'portrait' }) 107 | const isRetina = useMediaQuery({ minResolution: '2dppx' }) 108 | 109 | return
...
110 | } 111 | ``` 112 | 113 | ### Forcing a device with the `device` prop 114 | 115 | At times you may need to render components with different device settings than what gets automatically detected. This is especially useful in a Node environment where these settings can't be detected (SSR) or for testing. 116 | 117 | #### Possible Keys 118 | 119 | `orientation`, `scan`, `aspectRatio`, `deviceAspectRatio`, 120 | `height`, `deviceHeight`, `width`, `deviceWidth`, `color`, `colorIndex`, `monochrome`, 121 | `resolution` and `type` 122 | 123 | ##### Possible Types 124 | 125 | `type` can be one of: `all`, `grid`, `aural`, `braille`, `handheld`, `print`, `projection`, 126 | `screen`, `tty`, `tv` or `embossed` 127 | 128 | Note: The `device` property always applies, even when it can be detected (where window.matchMedia exists). 129 | 130 | ```jsx 131 | import { useMediaQuery } from 'react-responsive' 132 | 133 | const Example = () => { 134 | const isDesktopOrLaptop = useMediaQuery( 135 | { minDeviceWidth: 1224 }, 136 | { deviceWidth: 1600 } // `device` prop 137 | ) 138 | 139 | return ( 140 |
141 | {isDesktopOrLaptop && ( 142 |

143 | this will always get rendered even if device is shorter than 1224px, 144 | that's because we overrode device settings with 'deviceWidth: 1600'. 145 |

146 | )} 147 |
148 | ) 149 | } 150 | ``` 151 | 152 | #### Supplying through Context 153 | 154 | You can also pass `device` to every `useMediaQuery` hook in the components tree through a React [Context](https://reactjs.org/docs/context.html). 155 | This should ease up server-side-rendering and testing in a Node environment, e.g: 156 | 157 | ##### Server-Side Rendering 158 | 159 | ```jsx 160 | import { Context as ResponsiveContext } from 'react-responsive' 161 | import { renderToString } from 'react-dom/server' 162 | import App from './App' 163 | 164 | ... 165 | // Context is just a regular React Context component, it accepts a `value` prop to be passed to consuming components 166 | const mobileApp = renderToString( 167 | 168 | 169 | 170 | ) 171 | ... 172 | ``` 173 | 174 | If you use next.js, structure your import like this to disable server-side rendering for components that use this library: 175 | 176 | ```js 177 | import dynamic from 'next/dynamic' 178 | const MediaQuery = dynamic(() => import('react-responsive'), { 179 | ssr: false 180 | }) 181 | ``` 182 | 183 | ##### Testing 184 | 185 | ```jsx 186 | import { Context as ResponsiveContext } from 'react-responsive' 187 | import { render } from '@testing-library/react' 188 | import ProductsListing from './ProductsListing' 189 | 190 | describe('ProductsListing', () => { 191 | test('matches the snapshot', () => { 192 | const { container: mobile } = render( 193 | 194 | 195 | 196 | ) 197 | expect(mobile).toMatchSnapshot() 198 | 199 | const { container: desktop } = render( 200 | 201 | 202 | 203 | ) 204 | expect(desktop).toMatchSnapshot() 205 | }) 206 | }) 207 | ``` 208 | 209 | Note that if anything has a `device` prop passed in it will take precedence over the one from context. 210 | 211 | ### `onChange` 212 | 213 | You can use the `onChange` callback to specify a change handler that will be called when the media query's value changes. 214 | 215 | ```jsx 216 | import React from 'react' 217 | import { useMediaQuery } from 'react-responsive' 218 | 219 | const Example = () => { 220 | const handleMediaQueryChange = (matches) => { 221 | // matches will be true or false based on the value for the media query 222 | } 223 | const isDesktopOrLaptop = useMediaQuery( 224 | { minWidth: 1224 }, 225 | undefined, 226 | handleMediaQueryChange 227 | ) 228 | 229 | return
...
230 | } 231 | ``` 232 | 233 | ```jsx 234 | import React from 'react' 235 | import MediaQuery from 'react-responsive' 236 | 237 | const Example = () => { 238 | const handleMediaQueryChange = (matches) => { 239 | // matches will be true or false based on the value for the media query 240 | } 241 | 242 | return ( 243 | 244 | ... 245 | 246 | ) 247 | } 248 | ``` 249 | 250 | ## Easy Mode 251 | 252 | That's it! Now you can create your application specific breakpoints and reuse them easily. Here is an example: 253 | 254 | ```jsx 255 | import { useMediaQuery } from 'react-responsive' 256 | 257 | const Desktop = ({ children }) => { 258 | const isDesktop = useMediaQuery({ minWidth: 992 }) 259 | return isDesktop ? children : null 260 | } 261 | const Tablet = ({ children }) => { 262 | const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 991 }) 263 | return isTablet ? children : null 264 | } 265 | const Mobile = ({ children }) => { 266 | const isMobile = useMediaQuery({ maxWidth: 767 }) 267 | return isMobile ? children : null 268 | } 269 | const Default = ({ children }) => { 270 | const isNotMobile = useMediaQuery({ minWidth: 768 }) 271 | return isNotMobile ? children : null 272 | } 273 | 274 | const Example = () => ( 275 |
276 | Desktop or laptop 277 | Tablet 278 | Mobile 279 | Not mobile (desktop or laptop or tablet) 280 |
281 | ) 282 | 283 | export default Example 284 | ``` 285 | 286 | And if you want a combo (the DRY way): 287 | 288 | ```js 289 | import { useMediaQuery } from 'react-responsive' 290 | 291 | const useDesktopMediaQuery = () => 292 | useMediaQuery({ query: '(min-width: 1280px)' }) 293 | 294 | const useTabletAndBelowMediaQuery = () => 295 | useMediaQuery({ query: '(max-width: 1279px)' }) 296 | 297 | const Desktop = ({ children }) => { 298 | const isDesktop = useDesktopMediaQuery() 299 | 300 | return isDesktop ? children : null 301 | } 302 | 303 | const TabletAndBelow = ({ children }) => { 304 | const isTabletAndBelow = useTabletAndBelowMediaQuery() 305 | 306 | return isTabletAndBelow ? children : null 307 | } 308 | ``` 309 | 310 | ## Browser Support 311 | 312 | ### Out of the box 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 |
Chrome9
Firefox (Gecko)6
MS EdgeAll
Internet Explorer10
Opera12.1
Safari5.1
340 | 341 | ### With Polyfills 342 | 343 | Pretty much everything. Check out these polyfills: 344 | 345 | - [matchMedia.js by Paul Irish](https://github.com/paulirish/matchMedia.js/) 346 | - [media-match (faster, but larger and lacking some features)](https://github.com/weblinc/media-match) 347 | 348 | [downloads-image]: http://img.shields.io/npm/dm/react-responsive.svg 349 | [npm-url]: https://npmjs.org/package/react-responsive 350 | [npm-image]: http://img.shields.io/npm/v/react-responsive.svg 351 | -------------------------------------------------------------------------------- /dist/cjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | var react = require('react'); 6 | var matchMedia = require('matchmediaquery'); 7 | var hyphenate = require('hyphenate-style-name'); 8 | var shallowEqual = require('shallow-equal'); 9 | var PropTypes = require('prop-types'); 10 | 11 | const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); 12 | // media types 13 | const types = { 14 | all: PropTypes.bool, 15 | grid: PropTypes.bool, 16 | aural: PropTypes.bool, 17 | braille: PropTypes.bool, 18 | handheld: PropTypes.bool, 19 | print: PropTypes.bool, 20 | projection: PropTypes.bool, 21 | screen: PropTypes.bool, 22 | tty: PropTypes.bool, 23 | tv: PropTypes.bool, 24 | embossed: PropTypes.bool 25 | }; 26 | // properties that match media queries 27 | const matchers = { 28 | orientation: PropTypes.oneOf(['portrait', 'landscape']), 29 | scan: PropTypes.oneOf(['progressive', 'interlace']), 30 | aspectRatio: PropTypes.string, 31 | deviceAspectRatio: PropTypes.string, 32 | height: stringOrNumber, 33 | deviceHeight: stringOrNumber, 34 | width: stringOrNumber, 35 | deviceWidth: stringOrNumber, 36 | color: PropTypes.bool, 37 | colorIndex: PropTypes.bool, 38 | monochrome: PropTypes.bool, 39 | resolution: stringOrNumber, 40 | type: Object.keys(types) 41 | }; 42 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 43 | const { type, ...featureMatchers } = matchers; 44 | // media features 45 | const features = { 46 | minAspectRatio: PropTypes.string, 47 | maxAspectRatio: PropTypes.string, 48 | minDeviceAspectRatio: PropTypes.string, 49 | maxDeviceAspectRatio: PropTypes.string, 50 | minHeight: stringOrNumber, 51 | maxHeight: stringOrNumber, 52 | minDeviceHeight: stringOrNumber, 53 | maxDeviceHeight: stringOrNumber, 54 | minWidth: stringOrNumber, 55 | maxWidth: stringOrNumber, 56 | minDeviceWidth: stringOrNumber, 57 | maxDeviceWidth: stringOrNumber, 58 | minColor: PropTypes.number, 59 | maxColor: PropTypes.number, 60 | minColorIndex: PropTypes.number, 61 | maxColorIndex: PropTypes.number, 62 | minMonochrome: PropTypes.number, 63 | maxMonochrome: PropTypes.number, 64 | minResolution: stringOrNumber, 65 | maxResolution: stringOrNumber, 66 | ...featureMatchers 67 | }; 68 | const all = { ...types, ...features }; 69 | var mq = { 70 | all: all, 71 | types: types, 72 | matchers: matchers, 73 | features: features 74 | }; 75 | 76 | const negate = (cond) => `not ${cond}`; 77 | const keyVal = (k, v) => { 78 | const realKey = hyphenate(k); 79 | // px shorthand 80 | if (typeof v === 'number') { 81 | v = `${v}px`; 82 | } 83 | if (v === true) { 84 | return realKey; 85 | } 86 | if (v === false) { 87 | return negate(realKey); 88 | } 89 | return `(${realKey}: ${v})`; 90 | }; 91 | const join = (conds) => conds.join(' and '); 92 | const toQuery = (obj) => { 93 | const rules = []; 94 | Object.keys(mq.all).forEach((k) => { 95 | const v = obj[k]; 96 | if (v != null) { 97 | rules.push(keyVal(k, v)); 98 | } 99 | }); 100 | return join(rules); 101 | }; 102 | 103 | const Context = react.createContext(undefined); 104 | 105 | const makeQuery = (settings) => settings.query || toQuery(settings); 106 | const hyphenateKeys = (obj) => { 107 | if (!obj) 108 | return undefined; 109 | const keys = Object.keys(obj); 110 | return keys.reduce((result, key) => { 111 | result[hyphenate(key)] = obj[key]; 112 | return result; 113 | }, {}); 114 | }; 115 | const useIsUpdate = () => { 116 | const ref = react.useRef(false); 117 | react.useEffect(() => { 118 | ref.current = true; 119 | }, []); 120 | return ref.current; 121 | }; 122 | const useDevice = (deviceFromProps) => { 123 | const deviceFromContext = react.useContext(Context); 124 | const getDevice = () => hyphenateKeys(deviceFromProps) || hyphenateKeys(deviceFromContext); 125 | const [device, setDevice] = react.useState(getDevice); 126 | react.useEffect(() => { 127 | const newDevice = getDevice(); 128 | if (!shallowEqual.shallowEqualObjects(device, newDevice)) { 129 | setDevice(newDevice); 130 | } 131 | }, [deviceFromProps, deviceFromContext]); 132 | return device; 133 | }; 134 | const useQuery = (settings) => { 135 | const getQuery = () => makeQuery(settings); 136 | const [query, setQuery] = react.useState(getQuery); 137 | react.useEffect(() => { 138 | const newQuery = getQuery(); 139 | if (query !== newQuery) { 140 | setQuery(newQuery); 141 | } 142 | }, [settings]); 143 | return query; 144 | }; 145 | const useMatchMedia = (query, device) => { 146 | const getMatchMedia = () => matchMedia(query, device || {}, !!device); 147 | const [mq, setMq] = react.useState(getMatchMedia); 148 | const isUpdate = useIsUpdate(); 149 | react.useEffect(() => { 150 | if (isUpdate) { 151 | // skip on mounting, it has already been set 152 | const newMq = getMatchMedia(); 153 | setMq(newMq); 154 | return () => { 155 | if (newMq) { 156 | newMq.dispose(); 157 | } 158 | }; 159 | } 160 | }, [query, device]); 161 | return mq; 162 | }; 163 | const useMatches = (mediaQuery) => { 164 | const [matches, setMatches] = react.useState(mediaQuery.matches); 165 | react.useEffect(() => { 166 | const updateMatches = (ev) => { 167 | setMatches(ev.matches); 168 | }; 169 | mediaQuery.addListener(updateMatches); 170 | setMatches(mediaQuery.matches); 171 | return () => { 172 | mediaQuery.removeListener(updateMatches); 173 | }; 174 | }, [mediaQuery]); 175 | return matches; 176 | }; 177 | const useMediaQuery = (settings, device, onChange) => { 178 | const deviceSettings = useDevice(device); 179 | const query = useQuery(settings); 180 | if (!query) 181 | throw new Error('Invalid or missing MediaQuery!'); 182 | const mq = useMatchMedia(query, deviceSettings); 183 | const matches = useMatches(mq); 184 | const isUpdate = useIsUpdate(); 185 | react.useEffect(() => { 186 | if (isUpdate && onChange) { 187 | onChange(matches); 188 | } 189 | }, [matches]); 190 | react.useEffect(() => () => { 191 | if (mq) { 192 | mq.dispose(); 193 | } 194 | }, []); 195 | return matches; 196 | }; 197 | 198 | // ReactNode and ReactElement typings are a little funky for functional components, so the ReactElement cast is needed on the return 199 | const MediaQuery = ({ children, device, onChange, ...settings }) => { 200 | const matches = useMediaQuery(settings, device, onChange); 201 | if (typeof children === 'function') { 202 | return children(matches); 203 | } 204 | return matches ? children : null; 205 | }; 206 | 207 | exports.Context = Context; 208 | exports.MediaQuery = MediaQuery; 209 | exports.default = MediaQuery; 210 | exports.toQuery = toQuery; 211 | exports.useMediaQuery = useMediaQuery; 212 | //# sourceMappingURL=index.js.map 213 | -------------------------------------------------------------------------------- /dist/cjs/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":["../../src/mediaQuery.ts","../../src/toQuery.ts","../../src/Context.ts","../../src/useMediaQuery.ts","../../src/Component.ts"],"sourcesContent":[null,null,null,null,null],"names":["createContext","useRef","useEffect","useContext","useState","shallowEqualObjects"],"mappings":"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"} -------------------------------------------------------------------------------- /dist/esm/index.js: -------------------------------------------------------------------------------- 1 | import { createContext, useRef, useEffect, useContext, useState } from 'react'; 2 | import matchMedia from 'matchmediaquery'; 3 | import hyphenate from 'hyphenate-style-name'; 4 | import { shallowEqualObjects } from 'shallow-equal'; 5 | import PropTypes from 'prop-types'; 6 | 7 | const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); 8 | // media types 9 | const types = { 10 | all: PropTypes.bool, 11 | grid: PropTypes.bool, 12 | aural: PropTypes.bool, 13 | braille: PropTypes.bool, 14 | handheld: PropTypes.bool, 15 | print: PropTypes.bool, 16 | projection: PropTypes.bool, 17 | screen: PropTypes.bool, 18 | tty: PropTypes.bool, 19 | tv: PropTypes.bool, 20 | embossed: PropTypes.bool 21 | }; 22 | // properties that match media queries 23 | const matchers = { 24 | orientation: PropTypes.oneOf(['portrait', 'landscape']), 25 | scan: PropTypes.oneOf(['progressive', 'interlace']), 26 | aspectRatio: PropTypes.string, 27 | deviceAspectRatio: PropTypes.string, 28 | height: stringOrNumber, 29 | deviceHeight: stringOrNumber, 30 | width: stringOrNumber, 31 | deviceWidth: stringOrNumber, 32 | color: PropTypes.bool, 33 | colorIndex: PropTypes.bool, 34 | monochrome: PropTypes.bool, 35 | resolution: stringOrNumber, 36 | type: Object.keys(types) 37 | }; 38 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 39 | const { type, ...featureMatchers } = matchers; 40 | // media features 41 | const features = { 42 | minAspectRatio: PropTypes.string, 43 | maxAspectRatio: PropTypes.string, 44 | minDeviceAspectRatio: PropTypes.string, 45 | maxDeviceAspectRatio: PropTypes.string, 46 | minHeight: stringOrNumber, 47 | maxHeight: stringOrNumber, 48 | minDeviceHeight: stringOrNumber, 49 | maxDeviceHeight: stringOrNumber, 50 | minWidth: stringOrNumber, 51 | maxWidth: stringOrNumber, 52 | minDeviceWidth: stringOrNumber, 53 | maxDeviceWidth: stringOrNumber, 54 | minColor: PropTypes.number, 55 | maxColor: PropTypes.number, 56 | minColorIndex: PropTypes.number, 57 | maxColorIndex: PropTypes.number, 58 | minMonochrome: PropTypes.number, 59 | maxMonochrome: PropTypes.number, 60 | minResolution: stringOrNumber, 61 | maxResolution: stringOrNumber, 62 | ...featureMatchers 63 | }; 64 | const all = { ...types, ...features }; 65 | var mq = { 66 | all: all, 67 | types: types, 68 | matchers: matchers, 69 | features: features 70 | }; 71 | 72 | const negate = (cond) => `not ${cond}`; 73 | const keyVal = (k, v) => { 74 | const realKey = hyphenate(k); 75 | // px shorthand 76 | if (typeof v === 'number') { 77 | v = `${v}px`; 78 | } 79 | if (v === true) { 80 | return realKey; 81 | } 82 | if (v === false) { 83 | return negate(realKey); 84 | } 85 | return `(${realKey}: ${v})`; 86 | }; 87 | const join = (conds) => conds.join(' and '); 88 | const toQuery = (obj) => { 89 | const rules = []; 90 | Object.keys(mq.all).forEach((k) => { 91 | const v = obj[k]; 92 | if (v != null) { 93 | rules.push(keyVal(k, v)); 94 | } 95 | }); 96 | return join(rules); 97 | }; 98 | 99 | const Context = createContext(undefined); 100 | 101 | const makeQuery = (settings) => settings.query || toQuery(settings); 102 | const hyphenateKeys = (obj) => { 103 | if (!obj) 104 | return undefined; 105 | const keys = Object.keys(obj); 106 | return keys.reduce((result, key) => { 107 | result[hyphenate(key)] = obj[key]; 108 | return result; 109 | }, {}); 110 | }; 111 | const useIsUpdate = () => { 112 | const ref = useRef(false); 113 | useEffect(() => { 114 | ref.current = true; 115 | }, []); 116 | return ref.current; 117 | }; 118 | const useDevice = (deviceFromProps) => { 119 | const deviceFromContext = useContext(Context); 120 | const getDevice = () => hyphenateKeys(deviceFromProps) || hyphenateKeys(deviceFromContext); 121 | const [device, setDevice] = useState(getDevice); 122 | useEffect(() => { 123 | const newDevice = getDevice(); 124 | if (!shallowEqualObjects(device, newDevice)) { 125 | setDevice(newDevice); 126 | } 127 | }, [deviceFromProps, deviceFromContext]); 128 | return device; 129 | }; 130 | const useQuery = (settings) => { 131 | const getQuery = () => makeQuery(settings); 132 | const [query, setQuery] = useState(getQuery); 133 | useEffect(() => { 134 | const newQuery = getQuery(); 135 | if (query !== newQuery) { 136 | setQuery(newQuery); 137 | } 138 | }, [settings]); 139 | return query; 140 | }; 141 | const useMatchMedia = (query, device) => { 142 | const getMatchMedia = () => matchMedia(query, device || {}, !!device); 143 | const [mq, setMq] = useState(getMatchMedia); 144 | const isUpdate = useIsUpdate(); 145 | useEffect(() => { 146 | if (isUpdate) { 147 | // skip on mounting, it has already been set 148 | const newMq = getMatchMedia(); 149 | setMq(newMq); 150 | return () => { 151 | if (newMq) { 152 | newMq.dispose(); 153 | } 154 | }; 155 | } 156 | }, [query, device]); 157 | return mq; 158 | }; 159 | const useMatches = (mediaQuery) => { 160 | const [matches, setMatches] = useState(mediaQuery.matches); 161 | useEffect(() => { 162 | const updateMatches = (ev) => { 163 | setMatches(ev.matches); 164 | }; 165 | mediaQuery.addListener(updateMatches); 166 | setMatches(mediaQuery.matches); 167 | return () => { 168 | mediaQuery.removeListener(updateMatches); 169 | }; 170 | }, [mediaQuery]); 171 | return matches; 172 | }; 173 | const useMediaQuery = (settings, device, onChange) => { 174 | const deviceSettings = useDevice(device); 175 | const query = useQuery(settings); 176 | if (!query) 177 | throw new Error('Invalid or missing MediaQuery!'); 178 | const mq = useMatchMedia(query, deviceSettings); 179 | const matches = useMatches(mq); 180 | const isUpdate = useIsUpdate(); 181 | useEffect(() => { 182 | if (isUpdate && onChange) { 183 | onChange(matches); 184 | } 185 | }, [matches]); 186 | useEffect(() => () => { 187 | if (mq) { 188 | mq.dispose(); 189 | } 190 | }, []); 191 | return matches; 192 | }; 193 | 194 | // ReactNode and ReactElement typings are a little funky for functional components, so the ReactElement cast is needed on the return 195 | const MediaQuery = ({ children, device, onChange, ...settings }) => { 196 | const matches = useMediaQuery(settings, device, onChange); 197 | if (typeof children === 'function') { 198 | return children(matches); 199 | } 200 | return matches ? children : null; 201 | }; 202 | 203 | export { Context, MediaQuery, MediaQuery as default, toQuery, useMediaQuery }; 204 | //# sourceMappingURL=index.js.map 205 | -------------------------------------------------------------------------------- /dist/esm/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":["../../src/mediaQuery.ts","../../src/toQuery.ts","../../src/Context.ts","../../src/useMediaQuery.ts","../../src/Component.ts"],"sourcesContent":[null,null,null,null,null],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/types/Component.d.ts: -------------------------------------------------------------------------------- 1 | import { ReactNode, FC, CSSProperties } from 'react'; 2 | import { MediaQueryAllQueryable, MediaQueryMatchers } from './types'; 3 | interface MediaQueryProps extends MediaQueryAllQueryable { 4 | component?: ReactNode; 5 | children?: ReactNode | ((matches: boolean) => ReactNode); 6 | query?: string; 7 | style?: CSSProperties; 8 | className?: string; 9 | device?: MediaQueryMatchers; 10 | values?: Partial; 11 | onBeforeChange?: (_matches: boolean) => void; 12 | onChange?: (_matches: boolean) => void; 13 | } 14 | declare const MediaQuery: FC; 15 | export default MediaQuery; 16 | -------------------------------------------------------------------------------- /dist/types/Context.d.ts: -------------------------------------------------------------------------------- 1 | import { MediaQueryAllQueryable } from './types'; 2 | declare const Context: import("react").Context | undefined>; 3 | export default Context; 4 | -------------------------------------------------------------------------------- /dist/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import useMediaQuery from './useMediaQuery'; 2 | import MediaQuery from './Component'; 3 | import toQuery from './toQuery'; 4 | import Context from './Context'; 5 | export { MediaQuery as default, MediaQuery, useMediaQuery, toQuery, Context }; 6 | export type { MediaQueryTypes, MediaQueryType, MediaQueryFeatures, MediaQueryMatchers, MediaQueryAllQueryable } from './types'; 7 | -------------------------------------------------------------------------------- /dist/types/mediaQuery.d.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | declare const _default: { 3 | all: { 4 | orientation: PropTypes.Requireable; 5 | scan: PropTypes.Requireable; 6 | aspectRatio: PropTypes.Requireable; 7 | deviceAspectRatio: PropTypes.Requireable; 8 | height: PropTypes.Requireable>; 9 | deviceHeight: PropTypes.Requireable>; 10 | width: PropTypes.Requireable>; 11 | deviceWidth: PropTypes.Requireable>; 12 | color: PropTypes.Requireable; 13 | colorIndex: PropTypes.Requireable; 14 | monochrome: PropTypes.Requireable; 15 | resolution: PropTypes.Requireable>; 16 | minAspectRatio: PropTypes.Requireable; 17 | maxAspectRatio: PropTypes.Requireable; 18 | minDeviceAspectRatio: PropTypes.Requireable; 19 | maxDeviceAspectRatio: PropTypes.Requireable; 20 | minHeight: PropTypes.Requireable>; 21 | maxHeight: PropTypes.Requireable>; 22 | minDeviceHeight: PropTypes.Requireable>; 23 | maxDeviceHeight: PropTypes.Requireable>; 24 | minWidth: PropTypes.Requireable>; 25 | maxWidth: PropTypes.Requireable>; 26 | minDeviceWidth: PropTypes.Requireable>; 27 | maxDeviceWidth: PropTypes.Requireable>; 28 | minColor: PropTypes.Requireable; 29 | maxColor: PropTypes.Requireable; 30 | minColorIndex: PropTypes.Requireable; 31 | maxColorIndex: PropTypes.Requireable; 32 | minMonochrome: PropTypes.Requireable; 33 | maxMonochrome: PropTypes.Requireable; 34 | minResolution: PropTypes.Requireable>; 35 | maxResolution: PropTypes.Requireable>; 36 | all: PropTypes.Requireable; 37 | grid: PropTypes.Requireable; 38 | aural: PropTypes.Requireable; 39 | braille: PropTypes.Requireable; 40 | handheld: PropTypes.Requireable; 41 | print: PropTypes.Requireable; 42 | projection: PropTypes.Requireable; 43 | screen: PropTypes.Requireable; 44 | tty: PropTypes.Requireable; 45 | tv: PropTypes.Requireable; 46 | embossed: PropTypes.Requireable; 47 | }; 48 | types: { 49 | all: PropTypes.Requireable; 50 | grid: PropTypes.Requireable; 51 | aural: PropTypes.Requireable; 52 | braille: PropTypes.Requireable; 53 | handheld: PropTypes.Requireable; 54 | print: PropTypes.Requireable; 55 | projection: PropTypes.Requireable; 56 | screen: PropTypes.Requireable; 57 | tty: PropTypes.Requireable; 58 | tv: PropTypes.Requireable; 59 | embossed: PropTypes.Requireable; 60 | }; 61 | matchers: { 62 | orientation: PropTypes.Requireable; 63 | scan: PropTypes.Requireable; 64 | aspectRatio: PropTypes.Requireable; 65 | deviceAspectRatio: PropTypes.Requireable; 66 | height: PropTypes.Requireable>; 67 | deviceHeight: PropTypes.Requireable>; 68 | width: PropTypes.Requireable>; 69 | deviceWidth: PropTypes.Requireable>; 70 | color: PropTypes.Requireable; 71 | colorIndex: PropTypes.Requireable; 72 | monochrome: PropTypes.Requireable; 73 | resolution: PropTypes.Requireable>; 74 | type: string[]; 75 | }; 76 | features: { 77 | orientation: PropTypes.Requireable; 78 | scan: PropTypes.Requireable; 79 | aspectRatio: PropTypes.Requireable; 80 | deviceAspectRatio: PropTypes.Requireable; 81 | height: PropTypes.Requireable>; 82 | deviceHeight: PropTypes.Requireable>; 83 | width: PropTypes.Requireable>; 84 | deviceWidth: PropTypes.Requireable>; 85 | color: PropTypes.Requireable; 86 | colorIndex: PropTypes.Requireable; 87 | monochrome: PropTypes.Requireable; 88 | resolution: PropTypes.Requireable>; 89 | minAspectRatio: PropTypes.Requireable; 90 | maxAspectRatio: PropTypes.Requireable; 91 | minDeviceAspectRatio: PropTypes.Requireable; 92 | maxDeviceAspectRatio: PropTypes.Requireable; 93 | minHeight: PropTypes.Requireable>; 94 | maxHeight: PropTypes.Requireable>; 95 | minDeviceHeight: PropTypes.Requireable>; 96 | maxDeviceHeight: PropTypes.Requireable>; 97 | minWidth: PropTypes.Requireable>; 98 | maxWidth: PropTypes.Requireable>; 99 | minDeviceWidth: PropTypes.Requireable>; 100 | maxDeviceWidth: PropTypes.Requireable>; 101 | minColor: PropTypes.Requireable; 102 | maxColor: PropTypes.Requireable; 103 | minColorIndex: PropTypes.Requireable; 104 | maxColorIndex: PropTypes.Requireable; 105 | minMonochrome: PropTypes.Requireable; 106 | maxMonochrome: PropTypes.Requireable; 107 | minResolution: PropTypes.Requireable>; 108 | maxResolution: PropTypes.Requireable>; 109 | }; 110 | }; 111 | export default _default; 112 | -------------------------------------------------------------------------------- /dist/types/toQuery.d.ts: -------------------------------------------------------------------------------- 1 | import { MediaQueryAllQueryable } from './types'; 2 | declare const toQuery: (obj: Partial) => string; 3 | export default toQuery; 4 | -------------------------------------------------------------------------------- /dist/types/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface MediaQueryTypes { 2 | all?: boolean; 3 | grid?: boolean; 4 | aural?: boolean; 5 | braille?: boolean; 6 | handheld?: boolean; 7 | print?: boolean; 8 | projection?: boolean; 9 | screen?: boolean; 10 | tty?: boolean; 11 | tv?: boolean; 12 | embossed?: boolean; 13 | } 14 | export type MediaQueryType = keyof MediaQueryTypes; 15 | export interface MediaQueryMatchers { 16 | aspectRatio?: string; 17 | deviceAspectRatio?: string; 18 | height?: number | string; 19 | deviceHeight?: number | string; 20 | width?: number | string; 21 | deviceWidth?: number | string; 22 | color?: boolean; 23 | colorIndex?: boolean; 24 | monochrome?: boolean; 25 | resolution?: number | string; 26 | orientation?: 'portrait' | 'landscape'; 27 | scan?: 'progressive' | 'interlace'; 28 | type?: MediaQueryType; 29 | } 30 | export interface MediaQueryFeatures extends MediaQueryMatchers { 31 | minAspectRatio?: string; 32 | maxAspectRatio?: string; 33 | minDeviceAspectRatio?: string; 34 | maxDeviceAspectRatio?: string; 35 | minHeight?: number | string; 36 | maxHeight?: number | string; 37 | minDeviceHeight?: number | string; 38 | maxDeviceHeight?: number | string; 39 | minWidth?: number | string; 40 | maxWidth?: number | string; 41 | minDeviceWidth?: number | string; 42 | maxDeviceWidth?: number | string; 43 | minColor?: number; 44 | maxColor?: number; 45 | minColorIndex?: number; 46 | maxColorIndex?: number; 47 | minMonochrome?: number; 48 | maxMonochrome?: number; 49 | minResolution?: number | string; 50 | maxResolution?: number | string; 51 | } 52 | export interface MediaQueryAllQueryable extends MediaQueryFeatures, MediaQueryTypes { 53 | } 54 | -------------------------------------------------------------------------------- /dist/types/useMediaQuery.d.ts: -------------------------------------------------------------------------------- 1 | import { MediaQueryAllQueryable, MediaQueryMatchers } from './types'; 2 | type MediaQuerySettings = Partial; 5 | declare const useMediaQuery: (settings: MediaQuerySettings, device?: MediaQueryMatchers, onChange?: (_: boolean) => void) => boolean; 6 | export default useMediaQuery; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-responsive", 3 | "description": "Media queries in react for responsive design", 4 | "version": "10.0.1", 5 | "homepage": "http://github.com/yocontra/react-responsive", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/yocontra/react-responsive.git" 9 | }, 10 | "author": "Contra (https://contra.io)", 11 | "license": "MIT", 12 | "main": "./dist/cjs/index.js", 13 | "module": "./dist/esm/index.js", 14 | "types": "./dist/types/index.d.ts", 15 | "sideEffects": false, 16 | "files": [ 17 | "dist", 18 | "src" 19 | ], 20 | "keywords": [ 21 | "css", 22 | "react-component", 23 | "viewport", 24 | "react", 25 | "mobile", 26 | "media queries", 27 | "respond", 28 | "media query", 29 | "matchMedia", 30 | "responsive", 31 | "component" 32 | ], 33 | "dependencies": { 34 | "hyphenate-style-name": "^1.0.0", 35 | "matchmediaquery": "^0.4.2", 36 | "prop-types": "^15.6.1", 37 | "shallow-equal": "^3.1.0" 38 | }, 39 | "peerDependencies": { 40 | "react": ">=16.8.0" 41 | }, 42 | "devDependencies": { 43 | "@rollup/plugin-typescript": "^12.0.0", 44 | "@types/chai": "^5.0.0", 45 | "@types/hyphenate-style-name": "^1.0.0", 46 | "@types/jsdom": "^21.0.0", 47 | "@types/match-media-mock": "^0.1.5", 48 | "@types/matchmediaquery": "^0.3.0", 49 | "@types/mocha": "^10.0.0", 50 | "@types/react": "^18.0.0", 51 | "@types/react-dom": "^18.0.0", 52 | "@types/sinon": "^17.0.0", 53 | "@typescript-eslint/eslint-plugin": "^8.0.0", 54 | "@typescript-eslint/parser": "^8.0.0", 55 | "chai": "^5.0.0", 56 | "cross-env": "^7.0.0", 57 | "eslint": "^9.0.0", 58 | "eslint-plugin-compat": "^6.0.0", 59 | "gh-pages": "^6.0.0", 60 | "jsdom": "^26.0.0", 61 | "match-media-mock": "^0.1.1", 62 | "mocha": "^11.0.0", 63 | "prettier": "^3.0.0", 64 | "react": "^18.0.0", 65 | "react-dom": "^18.0.0", 66 | "rimraf": "^6.0.1", 67 | "rollup": "^4.0.0", 68 | "rollup-plugin-node-externals": "^8.0.0", 69 | "should": "^13.2.1", 70 | "sinon": "^19.0.0", 71 | "tslib": "^2.6.2", 72 | "tsx": "^4.7.1", 73 | "typedoc": "^0.27.9", 74 | "typescript": "^5.4.2" 75 | }, 76 | "scripts": { 77 | "preversion": "npm run clean && npm run build", 78 | "postpublish": "npm run tag && npm run docs", 79 | "prebuild": "npm run clean", 80 | "build:types": "tsc --outDir ./dist/types --declaration --emitDeclarationOnly", 81 | "build:lib": "rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript", 82 | "build": "npm run build:lib && npm run build:types", 83 | "build:watch": "npm run build -- --watch", 84 | "clean": "rimraf dist", 85 | "tag": "git tag -a \"v$npm_package_version\" -m \"tag version $npm_package_version\" && git push origin master --tags", 86 | "lint": "eslint --ext=ts,tsx . src test --fix && prettier . src test --write", 87 | "test": "npx mocha -R spec ./test/setup.js test/*_test.{ts,tsx}", 88 | "docs": "typedoc src/index.ts && gh-pages -d docs" 89 | }, 90 | "engines": { 91 | "node": ">=14" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /rollup.config.ts: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript' 2 | import externals from 'rollup-plugin-node-externals' 3 | import { defineConfig } from 'rollup' 4 | 5 | export default defineConfig({ 6 | input: './src/index.ts', 7 | treeshake: false, 8 | output: [ 9 | { 10 | dir: 'dist/cjs', 11 | format: 'commonjs', 12 | sourcemap: true, 13 | exports: 'named' 14 | }, 15 | { 16 | dir: 'dist/esm', 17 | format: 'esm', 18 | sourcemap: true 19 | } 20 | ], 21 | plugins: [externals(), typescript()] 22 | }) 23 | -------------------------------------------------------------------------------- /src/Component.ts: -------------------------------------------------------------------------------- 1 | import useMediaQuery from './useMediaQuery' 2 | import { ReactNode, ReactElement, FC, CSSProperties } from 'react' 3 | import { MediaQueryAllQueryable, MediaQueryMatchers } from './types' 4 | 5 | interface MediaQueryProps extends MediaQueryAllQueryable { 6 | component?: ReactNode 7 | children?: ReactNode | ((matches: boolean) => ReactNode) 8 | query?: string 9 | style?: CSSProperties 10 | className?: string 11 | device?: MediaQueryMatchers 12 | values?: Partial 13 | onBeforeChange?: (_matches: boolean) => void 14 | onChange?: (_matches: boolean) => void 15 | } 16 | 17 | // ReactNode and ReactElement typings are a little funky for functional components, so the ReactElement cast is needed on the return 18 | const MediaQuery: FC = ({ 19 | children, 20 | device, 21 | onChange, 22 | ...settings 23 | }) => { 24 | const matches = useMediaQuery(settings, device, onChange) 25 | 26 | if (typeof children === 'function') { 27 | return children(matches) as ReactElement 28 | } 29 | return matches ? (children as ReactElement) : null 30 | } 31 | 32 | export default MediaQuery 33 | -------------------------------------------------------------------------------- /src/Context.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { MediaQueryAllQueryable } from './types' 3 | 4 | const Context = createContext | undefined>( 5 | undefined 6 | ) 7 | 8 | export default Context 9 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import useMediaQuery from './useMediaQuery' 2 | import MediaQuery from './Component' 3 | import toQuery from './toQuery' 4 | import Context from './Context' 5 | 6 | export { MediaQuery as default, MediaQuery, useMediaQuery, toQuery, Context } 7 | 8 | export type { 9 | MediaQueryTypes, 10 | MediaQueryType, 11 | MediaQueryFeatures, 12 | MediaQueryMatchers, 13 | MediaQueryAllQueryable 14 | } from './types' 15 | -------------------------------------------------------------------------------- /src/mediaQuery.ts: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]) 4 | 5 | // media types 6 | const types = { 7 | all: PropTypes.bool, 8 | grid: PropTypes.bool, 9 | aural: PropTypes.bool, 10 | braille: PropTypes.bool, 11 | handheld: PropTypes.bool, 12 | print: PropTypes.bool, 13 | projection: PropTypes.bool, 14 | screen: PropTypes.bool, 15 | tty: PropTypes.bool, 16 | tv: PropTypes.bool, 17 | embossed: PropTypes.bool 18 | } 19 | 20 | // properties that match media queries 21 | const matchers = { 22 | orientation: PropTypes.oneOf(['portrait', 'landscape']), 23 | 24 | scan: PropTypes.oneOf(['progressive', 'interlace']), 25 | 26 | aspectRatio: PropTypes.string, 27 | deviceAspectRatio: PropTypes.string, 28 | 29 | height: stringOrNumber, 30 | deviceHeight: stringOrNumber, 31 | 32 | width: stringOrNumber, 33 | deviceWidth: stringOrNumber, 34 | 35 | color: PropTypes.bool, 36 | 37 | colorIndex: PropTypes.bool, 38 | 39 | monochrome: PropTypes.bool, 40 | resolution: stringOrNumber, 41 | type: Object.keys(types) 42 | } 43 | 44 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 45 | const { type, ...featureMatchers } = matchers 46 | 47 | // media features 48 | const features = { 49 | minAspectRatio: PropTypes.string, 50 | maxAspectRatio: PropTypes.string, 51 | minDeviceAspectRatio: PropTypes.string, 52 | maxDeviceAspectRatio: PropTypes.string, 53 | 54 | minHeight: stringOrNumber, 55 | maxHeight: stringOrNumber, 56 | minDeviceHeight: stringOrNumber, 57 | maxDeviceHeight: stringOrNumber, 58 | 59 | minWidth: stringOrNumber, 60 | maxWidth: stringOrNumber, 61 | minDeviceWidth: stringOrNumber, 62 | maxDeviceWidth: stringOrNumber, 63 | 64 | minColor: PropTypes.number, 65 | maxColor: PropTypes.number, 66 | 67 | minColorIndex: PropTypes.number, 68 | maxColorIndex: PropTypes.number, 69 | 70 | minMonochrome: PropTypes.number, 71 | maxMonochrome: PropTypes.number, 72 | 73 | minResolution: stringOrNumber, 74 | maxResolution: stringOrNumber, 75 | 76 | ...featureMatchers 77 | } 78 | 79 | const all = { ...types, ...features } 80 | 81 | export default { 82 | all: all, 83 | types: types, 84 | matchers: matchers, 85 | features: features 86 | } 87 | -------------------------------------------------------------------------------- /src/toQuery.ts: -------------------------------------------------------------------------------- 1 | import hyphenate from 'hyphenate-style-name' 2 | import mq from './mediaQuery' 3 | import { MediaQueryAllQueryable } from './types' 4 | 5 | const negate = (cond: string) => `not ${cond}` 6 | 7 | const keyVal = (k: string, v: unknown): string => { 8 | const realKey = hyphenate(k) 9 | 10 | // px shorthand 11 | if (typeof v === 'number') { 12 | v = `${v}px` 13 | } 14 | if (v === true) { 15 | return realKey 16 | } 17 | if (v === false) { 18 | return negate(realKey) 19 | } 20 | return `(${realKey}: ${v})` 21 | } 22 | 23 | const join = (conds: string[]): string => conds.join(' and ') 24 | 25 | const toQuery = (obj: Partial): string => { 26 | const rules: string[] = [] 27 | Object.keys(mq.all).forEach((k) => { 28 | const v = obj[k as keyof MediaQueryAllQueryable] 29 | if (v != null) { 30 | rules.push(keyVal(k, v)) 31 | } 32 | }) 33 | return join(rules) 34 | } 35 | 36 | export default toQuery 37 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export interface MediaQueryTypes { 2 | all?: boolean 3 | grid?: boolean 4 | aural?: boolean 5 | braille?: boolean 6 | handheld?: boolean 7 | print?: boolean 8 | projection?: boolean 9 | screen?: boolean 10 | tty?: boolean 11 | tv?: boolean 12 | embossed?: boolean 13 | } 14 | 15 | export type MediaQueryType = keyof MediaQueryTypes 16 | 17 | export interface MediaQueryMatchers { 18 | aspectRatio?: string 19 | deviceAspectRatio?: string 20 | height?: number | string 21 | deviceHeight?: number | string 22 | width?: number | string 23 | deviceWidth?: number | string 24 | color?: boolean 25 | colorIndex?: boolean 26 | monochrome?: boolean 27 | resolution?: number | string 28 | orientation?: 'portrait' | 'landscape' 29 | scan?: 'progressive' | 'interlace' 30 | type?: MediaQueryType 31 | } 32 | 33 | export interface MediaQueryFeatures extends MediaQueryMatchers { 34 | minAspectRatio?: string 35 | maxAspectRatio?: string 36 | 37 | minDeviceAspectRatio?: string 38 | maxDeviceAspectRatio?: string 39 | 40 | minHeight?: number | string 41 | maxHeight?: number | string 42 | 43 | minDeviceHeight?: number | string 44 | maxDeviceHeight?: number | string 45 | 46 | minWidth?: number | string 47 | maxWidth?: number | string 48 | 49 | minDeviceWidth?: number | string 50 | maxDeviceWidth?: number | string 51 | 52 | minColor?: number 53 | maxColor?: number 54 | 55 | minColorIndex?: number 56 | maxColorIndex?: number 57 | 58 | minMonochrome?: number 59 | maxMonochrome?: number 60 | 61 | minResolution?: number | string 62 | maxResolution?: number | string 63 | } 64 | 65 | export interface MediaQueryAllQueryable 66 | extends MediaQueryFeatures, 67 | MediaQueryTypes {} 68 | -------------------------------------------------------------------------------- /src/useMediaQuery.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect, useContext, useState } from 'react' 2 | import matchMedia from 'matchmediaquery' 3 | import hyphenate from 'hyphenate-style-name' 4 | import { shallowEqualObjects } from 'shallow-equal' 5 | import toQuery from './toQuery' 6 | import Context from './Context' 7 | import { MediaQueryAllQueryable, MediaQueryMatchers } from './types' 8 | 9 | type MediaQuerySettings = Partial 10 | type HyphenateKeyTypes = MediaQueryMatchers | MediaQueryAllQueryable 11 | 12 | const makeQuery = (settings: MediaQuerySettings) => 13 | settings.query || toQuery(settings) 14 | 15 | const hyphenateKeys = (obj?: HyphenateKeyTypes) => { 16 | type K = keyof HyphenateKeyTypes 17 | 18 | if (!obj) return undefined 19 | const keys = Object.keys(obj) as K[] 20 | 21 | return keys.reduce( 22 | (result, key) => { 23 | result[hyphenate(key)] = obj[key] 24 | return result 25 | }, 26 | {} as Record 27 | ) 28 | } 29 | 30 | const useIsUpdate = () => { 31 | const ref = useRef(false) 32 | 33 | useEffect(() => { 34 | ref.current = true 35 | }, []) 36 | 37 | return ref.current 38 | } 39 | 40 | const useDevice = ( 41 | deviceFromProps?: MediaQueryMatchers 42 | ): Partial | undefined => { 43 | const deviceFromContext = useContext(Context) 44 | const getDevice = () => 45 | hyphenateKeys(deviceFromProps) || hyphenateKeys(deviceFromContext) 46 | const [device, setDevice] = useState(getDevice) 47 | 48 | useEffect(() => { 49 | const newDevice = getDevice() 50 | if (!shallowEqualObjects(device, newDevice)) { 51 | setDevice(newDevice) 52 | } 53 | }, [deviceFromProps, deviceFromContext]) 54 | 55 | return device 56 | } 57 | 58 | const useQuery = (settings: MediaQuerySettings) => { 59 | const getQuery = () => makeQuery(settings) 60 | const [query, setQuery] = useState(getQuery) 61 | 62 | useEffect(() => { 63 | const newQuery = getQuery() 64 | if (query !== newQuery) { 65 | setQuery(newQuery) 66 | } 67 | }, [settings]) 68 | 69 | return query 70 | } 71 | 72 | const useMatchMedia = (query: string, device?: MediaQueryMatchers) => { 73 | const getMatchMedia = () => matchMedia(query, device || {}, !!device) 74 | const [mq, setMq] = useState(getMatchMedia) 75 | const isUpdate = useIsUpdate() 76 | 77 | useEffect(() => { 78 | if (isUpdate) { 79 | // skip on mounting, it has already been set 80 | const newMq = getMatchMedia() 81 | setMq(newMq) 82 | 83 | return () => { 84 | if (newMq) { 85 | newMq.dispose() 86 | } 87 | } 88 | } 89 | }, [query, device]) 90 | 91 | return mq 92 | } 93 | 94 | const useMatches = (mediaQuery: MediaQueryList): boolean => { 95 | const [matches, setMatches] = useState(mediaQuery.matches) 96 | 97 | useEffect(() => { 98 | const updateMatches = (ev: MediaQueryListEvent) => { 99 | setMatches(ev.matches) 100 | } 101 | mediaQuery.addListener(updateMatches) 102 | setMatches(mediaQuery.matches) 103 | 104 | return () => { 105 | mediaQuery.removeListener(updateMatches) 106 | } 107 | }, [mediaQuery]) 108 | 109 | return matches 110 | } 111 | 112 | const useMediaQuery = ( 113 | settings: MediaQuerySettings, 114 | device?: MediaQueryMatchers, 115 | onChange?: (_: boolean) => void 116 | ) => { 117 | const deviceSettings = useDevice(device) 118 | const query = useQuery(settings) 119 | if (!query) throw new Error('Invalid or missing MediaQuery!') 120 | const mq = useMatchMedia(query, deviceSettings) 121 | const matches = useMatches(mq as unknown as MediaQueryList) 122 | const isUpdate = useIsUpdate() 123 | 124 | useEffect(() => { 125 | if (isUpdate && onChange) { 126 | onChange(matches) 127 | } 128 | }, [matches]) 129 | 130 | useEffect( 131 | () => () => { 132 | if (mq) { 133 | mq.dispose() 134 | } 135 | }, 136 | [] 137 | ) 138 | 139 | return matches 140 | } 141 | 142 | export default useMediaQuery 143 | -------------------------------------------------------------------------------- /test/Component_test.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import MediaQuery from '../src/Component' 3 | import { assert } from 'chai' 4 | import ReactDOM from 'react-dom/client' 5 | import sinon from 'sinon' 6 | import TestUtils from 'react-dom/test-utils' 7 | import { MatchMediaMock } from 'match-media-mock' 8 | 9 | interface MockWindow extends Window { 10 | matchMedia: MatchMediaMock 11 | } 12 | 13 | describe('Component', () => { 14 | beforeEach(() => { 15 | ;(window as unknown as MockWindow).matchMedia.setConfig({ 16 | type: 'screen', 17 | width: 1200, 18 | height: 800 19 | }) 20 | }) 21 | 22 | it('renders when media query matches', () => { 23 | class App extends React.Component { 24 | render = () => ( 25 | 26 |
27 | 28 | ) 29 | } 30 | 31 | const tree = TestUtils.renderIntoDocument() 32 | assert.isNotNull( 33 | TestUtils.findRenderedDOMComponentWithClass( 34 | tree as unknown as Component, 35 | 'childComponent' 36 | ) 37 | ) 38 | }) 39 | 40 | it('doesnt render when media query doesnt match', () => { 41 | class App extends React.Component { 42 | render = () => ( 43 | 44 |
45 | 46 | ) 47 | } 48 | 49 | const tree = TestUtils.renderIntoDocument() 50 | assert.throws( 51 | () => 52 | TestUtils.findRenderedDOMComponentWithTag( 53 | tree as unknown as Component, 54 | 'div' 55 | ), 56 | /Did not find exactly one match/ 57 | ) 58 | }) 59 | 60 | it('works with render prop', () => { 61 | const renderFunc = sinon.stub().returns(null) 62 | TestUtils.renderIntoDocument( 63 | {renderFunc} 64 | ) 65 | assert.isTrue(renderFunc.calledOnce) 66 | assert.isTrue(renderFunc.calledWith(true)) 67 | 68 | const renderFunc2 = sinon.stub().returns(null) 69 | TestUtils.renderIntoDocument( 70 | {renderFunc2} 71 | ) 72 | assert.isTrue(renderFunc2.calledOnce) 73 | assert.isTrue(renderFunc2.calledWith(false)) 74 | }) 75 | 76 | it('does not throw on unmount', () => { 77 | const container = document.createElement('div') 78 | const root = ReactDOM.createRoot(container) 79 | 80 | root.render( 81 | 82 |
83 | 84 | ) 85 | assert.doesNotThrow(() => root.unmount()) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /test/mediaQuery_test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import mediaQuery from '../src/mediaQuery' 3 | 4 | describe('mediaQuery', () => { 5 | it('has types and features in [all]', () => { 6 | assert.deepEqual( 7 | Object.keys(mediaQuery.all), 8 | Object.keys(mediaQuery.types).concat(Object.keys(mediaQuery.features)) 9 | ) 10 | }) 11 | it('has all media types', () => { 12 | const types = [ 13 | 'all', 14 | 'grid', 15 | 'aural', 16 | 'braille', 17 | 'handheld', 18 | 'print', 19 | 'projection', 20 | 'screen', 21 | 'tty', 22 | 'tv', 23 | 'embossed' 24 | ] 25 | assert.deepEqual(Object.keys(mediaQuery.types), types) 26 | }) 27 | it('has matchers', () => { 28 | const matchers = [ 29 | 'orientation', 30 | 'scan', 31 | 'aspectRatio', 32 | 'deviceAspectRatio', 33 | 'height', 34 | 'deviceHeight', 35 | 'width', 36 | 'deviceWidth', 37 | 'color', 38 | 'colorIndex', 39 | 'monochrome', 40 | 'resolution', 41 | 'type' 42 | ] 43 | assert.deepEqual(Object.keys(mediaQuery.matchers), matchers) 44 | }) 45 | it('has features', () => { 46 | const features = [ 47 | 'minAspectRatio', 48 | 'maxAspectRatio', 49 | 'minDeviceAspectRatio', 50 | 'maxDeviceAspectRatio', 51 | 'minHeight', 52 | 'maxHeight', 53 | 'minDeviceHeight', 54 | 'maxDeviceHeight', 55 | 'minWidth', 56 | 'maxWidth', 57 | 'minDeviceWidth', 58 | 'maxDeviceWidth', 59 | 'minColor', 60 | 'maxColor', 61 | 'minColorIndex', 62 | 'maxColorIndex', 63 | 'minMonochrome', 64 | 'maxMonochrome', 65 | 'minResolution', 66 | 'maxResolution' 67 | ] 68 | const keys = features.concat(Object.keys(mediaQuery.matchers)) 69 | keys.splice(keys.indexOf('type'), 1) 70 | 71 | assert.deepEqual(Object.keys(mediaQuery.features), keys) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require('jsdom') 2 | const matchMediaMock = require('match-media-mock') 3 | 4 | process.env.NODE_ENV = 'test' 5 | 6 | global.window = new JSDOM( 7 | '
', 8 | { 9 | url: 'http://test.page' 10 | } 11 | ).window 12 | global.document = window.document 13 | global.self = global.window 14 | global.navigator = global.window.navigator 15 | globalThis.IS_REACT_ACT_ENVIRONMENT = true //https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html 16 | global.window.matchMedia = matchMediaMock.create() 17 | 18 | global.requestAnimationFrame = function (callback) { 19 | setTimeout(callback, 0) 20 | } 21 | -------------------------------------------------------------------------------- /test/toQuery_test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai' 2 | import toQuery from '../src/toQuery' 3 | 4 | describe('toQuery', () => { 5 | it('makes number rules', () => { 6 | const q = toQuery({ minWidth: 760 }) 7 | assert.equal(q, '(min-width: 760px)') 8 | }) 9 | it('makes true rules', () => { 10 | const q = toQuery({ colorIndex: true }) 11 | assert.equal(q, 'color-index') 12 | }) 13 | it('makes negative rules', () => { 14 | const q = toQuery({ colorIndex: false }) 15 | assert.equal(q, 'not color-index') 16 | }) 17 | it('makes regular rules', () => { 18 | const q = toQuery({ orientation: 'portrait' }) 19 | assert.equal(q, '(orientation: portrait)') 20 | }) 21 | it('handles multiple rules', () => { 22 | const q = toQuery({ orientation: 'portrait', minWidth: 760 }) 23 | assert.equal(q, '(min-width: 760px) and (orientation: portrait)') 24 | }) 25 | it('handles multiple query rules', () => { 26 | const q = toQuery({ minWidth: 640, maxWidth: 760 }) 27 | assert.equal(q, '(min-width: 640px) and (max-width: 760px)') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/useMediaQuery_test.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import useMediaQuery from '../src/useMediaQuery' 4 | import Context from '../src/Context' 5 | import { assert } from 'chai' 6 | import sinon from 'sinon' 7 | import TestUtils from 'react-dom/test-utils' 8 | import { MatchMediaMock } from 'match-media-mock' 9 | import { MediaQueryAllQueryable } from '../src/types' 10 | 11 | interface MockWindow extends Window { 12 | matchMedia: MatchMediaMock 13 | } 14 | 15 | const sleep = (timeOut: number) => 16 | new Promise((resolve) => setTimeout(resolve, timeOut)) 17 | 18 | describe('useMediaQuery', () => { 19 | beforeEach(() => { 20 | ;(window as unknown as MockWindow).matchMedia.setConfig({ 21 | type: 'screen', 22 | width: 1200, 23 | height: 800 24 | }) 25 | }) 26 | 27 | it('builds query from props', () => { 28 | type ComponentProps = Partial 29 | function Component(props: ComponentProps) { 30 | const matches = useMediaQuery(props) 31 | return matches ?
: null 32 | } 33 | class App extends React.Component { 34 | render = () => 35 | } 36 | 37 | const tree = TestUtils.renderIntoDocument() 38 | assert.isNotNull( 39 | TestUtils.findRenderedDOMComponentWithClass( 40 | tree as unknown as React.Component, 41 | 'childComponent' 42 | ) 43 | ) 44 | 45 | const tree2 = TestUtils.renderIntoDocument() 46 | assert.throws( 47 | () => 48 | TestUtils.findRenderedDOMComponentWithTag( 49 | tree2 as unknown as React.Component, 50 | 'div' 51 | ), 52 | /Did not find exactly one match/ 53 | ) 54 | }) 55 | 56 | it('builds query from device prop', () => { 57 | type ComponentProps = Partial 58 | function Component(props: ComponentProps) { 59 | const matches = useMediaQuery(props, { orientation: 'landscape' }) 60 | return matches ?
: null 61 | } 62 | class App extends React.Component { 63 | render = () => 64 | } 65 | 66 | const tree = TestUtils.renderIntoDocument() 67 | assert.isNotNull( 68 | TestUtils.findRenderedDOMComponentWithClass( 69 | tree as unknown as Component, 70 | 'childComponent' 71 | ) 72 | ) 73 | 74 | const tree2 = TestUtils.renderIntoDocument() 75 | assert.throws( 76 | () => 77 | TestUtils.findRenderedDOMComponentWithTag( 78 | tree2 as unknown as Component, 79 | 'div' 80 | ), 81 | /Did not find exactly one match/ 82 | ) 83 | }) 84 | 85 | it('matches taking device prop with precedence', () => { 86 | type ComponentProps = Partial< 87 | MediaQueryAllQueryable & { query?: string; device: { width: number } } 88 | > 89 | 90 | function Component({ device }: ComponentProps) { 91 | const matches = useMediaQuery({ minWidth: 1000 }, device) 92 | return matches ?
: null 93 | } 94 | class App extends React.Component { 95 | render = () => 96 | } 97 | 98 | const tree = TestUtils.renderIntoDocument() 99 | assert.isNotNull( 100 | TestUtils.findRenderedDOMComponentWithClass( 101 | tree as unknown as React.Component, 102 | 'childComponent' 103 | ) 104 | ) 105 | 106 | const tree2 = TestUtils.renderIntoDocument() 107 | assert.throws( 108 | () => 109 | TestUtils.findRenderedDOMComponentWithTag( 110 | tree2 as unknown as React.Component, 111 | 'div' 112 | ), 113 | /Did not find exactly one match/ 114 | ) 115 | }) 116 | 117 | it('throws if theres no query', () => { 118 | function App() { 119 | useMediaQuery({}) 120 | return null 121 | } 122 | assert.throws( 123 | () => TestUtils.renderIntoDocument(), 124 | 'Invalid or missing MediaQuery!' 125 | ) 126 | }) 127 | 128 | it('throws if theres a bad query', () => { 129 | function App() { 130 | useMediaQuery({}) 131 | return null 132 | } 133 | assert.throws( 134 | () => TestUtils.renderIntoDocument(), 135 | 'Invalid or missing MediaQuery!' 136 | ) 137 | }) 138 | 139 | it('calls onChange callback on updates', () => { 140 | const container = document.createElement('div') 141 | const root = ReactDOM.createRoot(container) 142 | type ComponentProps = Partial 143 | function App({ onChange, ...settings }: ComponentProps) { 144 | useMediaQuery(settings, undefined, onChange) 145 | return null 146 | } 147 | const callback = sinon.spy(() => null) 148 | 149 | TestUtils.act(() => { 150 | root.render() 151 | }) 152 | 153 | // should still match so nothing has changed 154 | TestUtils.act(() => { 155 | root.render() 156 | }) 157 | 158 | TestUtils.act(() => { 159 | root.render() 160 | }) 161 | 162 | return sleep(0).then(() => { 163 | assert.isTrue(callback.calledOnce) 164 | }) 165 | }) 166 | 167 | it('uses query prop if it has one', () => { 168 | ;(window as unknown as MockWindow).matchMedia.setConfig({ 169 | width: 500 170 | }) 171 | 172 | type ComponentProps = Partial 173 | 174 | function Component({ query }: ComponentProps) { 175 | const matches = useMediaQuery({ query }) 176 | return matches ?
: null 177 | } 178 | class App extends React.Component { 179 | render = () => 180 | } 181 | 182 | const tree = TestUtils.renderIntoDocument() 183 | assert.isNotNull( 184 | TestUtils.findRenderedDOMComponentWithClass( 185 | tree as unknown as Component, 186 | 'childComponent' 187 | ) 188 | ) 189 | 190 | const tree2 = TestUtils.renderIntoDocument() 191 | assert.throws( 192 | () => 193 | TestUtils.findRenderedDOMComponentWithTag( 194 | tree2 as unknown as Component, 195 | 'div' 196 | ), 197 | /Did not find exactly one match/ 198 | ) 199 | }) 200 | 201 | it('renders using device prop from context', () => { 202 | function Component() { 203 | const matches = useMediaQuery({ maxWidth: 300 }) 204 | return matches ?
: null 205 | } 206 | 207 | type ComponentProps = Partial< 208 | MediaQueryAllQueryable & { device: { width: number } } 209 | > 210 | 211 | class App extends React.Component { 212 | render = () => ( 213 | 214 | 215 | 216 | ) 217 | } 218 | 219 | const tree1 = TestUtils.renderIntoDocument() 220 | assert.isNotNull( 221 | TestUtils.findRenderedDOMComponentWithClass( 222 | tree1 as unknown as Component, 223 | 'childComponent' 224 | ) 225 | ) 226 | 227 | const tree2 = TestUtils.renderIntoDocument() 228 | assert.throws( 229 | () => 230 | TestUtils.findRenderedDOMComponentWithClass( 231 | tree2 as unknown as React.Component, 232 | 'childComponent' 233 | ), 234 | /Did not find exactly one match/ 235 | ) 236 | }) 237 | 238 | it('renders taking direct device prop with precedence to device prop from context', () => { 239 | function Component() { 240 | const matches = useMediaQuery({ maxWidth: 300 }, { width: 100 }) 241 | return matches ?
: null 242 | } 243 | class App extends React.Component { 244 | render = () => ( 245 | 246 | 247 | 248 | ) 249 | } 250 | 251 | const tree = TestUtils.renderIntoDocument() 252 | assert.isNotNull( 253 | TestUtils.findRenderedDOMComponentWithClass( 254 | tree as unknown as React.Component, 255 | 'childComponent' 256 | ) 257 | ) 258 | }) 259 | 260 | it('should render only once when mounted', () => { 261 | let renderCount = 0 262 | function App() { 263 | useMediaQuery({ maxWidth: 300 }) 264 | renderCount += 1 265 | 266 | return null 267 | } 268 | 269 | TestUtils.act(() => { 270 | TestUtils.renderIntoDocument() 271 | }) 272 | 273 | assert.equal(renderCount, 1) 274 | }) 275 | }) 276 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "lib": ["dom", "es6"], 5 | "jsx": "react", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "strict": true, 10 | "rootDir": "./src", 11 | "declaration": false, 12 | "esModuleInterop": true 13 | }, 14 | "include": ["src"], 15 | "exclude": ["test", "node_modules"] 16 | } 17 | --------------------------------------------------------------------------------