Skip to content

Commit e2ecfb0

Browse files
authored
Merge pull request #26 from react18-tools/storage-prop
Storage-prop
2 parents aac2100 + 069aa2a commit e2ecfb0

File tree

14 files changed

+164
-86
lines changed

14 files changed

+164
-86
lines changed

README.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,43 @@ This project is inspired by `next-themes`. `next-themes` is an awesome package,
2525
- ✅ Doccumented with [Typedoc](https://react18-tools.github.io/react18-themes) ([Docs](https://react18-tools.github.io/react18-themes))
2626
- ✅ Use combinations of [data-th=""] and [data-color-scheme=""] for dark/light varients of themes
2727
- ✅ Use [data-csp=""] to style based on colorSchemePreference.
28+
- ✅ Starting from version 2.3, `localStorage` is used as default storage. **No** `cookies` by default. Use storage="cookies" for smooth initial rendering of server components.
2829

2930
Check out the [live example](https://react18-themes.vercel.app/).
3031

3132
## Install
3233

3334
```bash
3435
$ pnpm add react18-themes
35-
# or
36+
```
37+
38+
**or**
39+
40+
```bash
3641
$ npm install react18-themes
37-
# or
42+
```
43+
44+
**or**
45+
46+
```bash
3847
$ yarn add react18-themes
3948
```
4049

4150
## Want Lite Version? [![npm bundle size](https://img.shields.io/bundlephobia/minzip/react18-themes-lite)](https://www.npmjs.com/package/react18-themes-lite) [![Version](https://img.shields.io/npm/v/react18-themes-lite.svg?colorB=green)](https://www.npmjs.com/package/react18-themes-lite) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/dt/react18-themes-lite.svg)](https://www.npmjs.com/package/react18-themes-lite)
4251

4352
```bash
4453
$ pnpm add react18-themes-lite
45-
# or
54+
```
55+
56+
**or**
57+
58+
```bash
4659
$ npm install react18-themes-lite
47-
# or
60+
```
61+
62+
**or**
63+
64+
```bash
4865
$ yarn add react18-themes-lite
4966
```
5067

@@ -217,6 +234,10 @@ In a similar way, you can also force color scheme.
217234

218235
Forcing color scheme will apply your defaultDark or defaultLight theme, configurable via hooks.
219236

237+
## Changelog
238+
239+
Find changelog here [CHANGELOG.md](lib/react18-themes/CHANGELOG.md)
240+
220241
## Migrating from v1 to v2
221242

222243
#### Motivation:
@@ -226,7 +247,8 @@ For server side syncing, we need to use cookies and headers. This means that thi
226247
Take care of the following while migrating to `v2`.
227248

228249
- No changes required for projects not using `Next.js` app router or server components other than updating cookies policy if needed.
229-
- The persistent storage is realized with `cookies` in place of `localStorage`. (You might want to update cookies policy accordingly.)
250+
- ~~The persistent storage is realized with `cookies` in place of `localStorage`. (You might want to update cookies policy accordingly.)~~
251+
- Starting from version 2.3, persistent storage is again set to `localStorage`. You can change it to `cookies` or `sassionStorage` by passing `storage` prop to `<ThemeSwitcher />` component.
230252
- We have provided `NextJsSSGThemeSwitcher` in addition to `ServerSideWrapper` for `Next.js`. You no longer need to use a wrapper component which broke static generation and forced SSR.
231253
- Visit [With Next.js `app` router (Server Components)](#with-nextjs-app-router-server-components)
232254

examples/nextjs/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# nextjs-example
22

3+
## 0.0.13
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
9+
10+
311
## 0.0.12
412

513
### Patch Changes

examples/nextjs/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nextjs-example",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"private": true,
55
"scripts": {
66
"dev": "next dev --port 3002",
@@ -18,9 +18,9 @@
1818
},
1919
"devDependencies": {
2020
"@next/eslint-plugin-next": "^14.0.4",
21-
"@types/node": "^20.10.4",
22-
"@types/react": "^18.2.42",
23-
"@types/react-dom": "^18.2.17",
21+
"@types/node": "^20.10.6",
22+
"@types/react": "^18.2.46",
23+
"@types/react-dom": "^18.2.18",
2424
"eslint-config-custom": "workspace:*",
2525
"tsconfig": "workspace:*",
2626
"typescript": "^5.3.3"

examples/remix/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
### Patch Changes
66

7+
- Updated dependencies
8+
9+
10+
11+
## null
12+
13+
### Patch Changes
14+
715
- Updated dependencies
816
917

examples/remix/package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@
1111
},
1212
"dependencies": {
1313
"@mayank1513/fork-me": "^2.0.0",
14-
"@remix-run/css-bundle": "^2.3.1",
15-
"@remix-run/node": "^2.3.1",
16-
"@remix-run/react": "^2.3.1",
17-
"@remix-run/serve": "^2.3.1",
18-
"isbot": "^3.7.1",
19-
"persist-and-sync": "^1.1.1",
14+
"@remix-run/css-bundle": "^2.4.1",
15+
"@remix-run/node": "^2.4.1",
16+
"@remix-run/react": "^2.4.1",
17+
"@remix-run/serve": "^2.4.1",
18+
"isbot": "3.7.1-deprecated",
19+
"persist-and-sync": "^1.2.0",
2020
"react": "^18.2.0",
2121
"react-dom": "^18.2.0",
2222
"react18-themes": "workspace:*",
2323
"shared-ui": "workspace:*",
2424
"zustand": "^4.4.7"
2525
},
2626
"devDependencies": {
27-
"@remix-run/dev": "^2.3.1",
28-
"@remix-run/eslint-config": "^2.3.1",
29-
"@types/react": "^18.2.42",
30-
"@types/react-dom": "^18.2.17",
31-
"eslint": "^8.55.0",
27+
"@remix-run/dev": "^2.4.1",
28+
"@remix-run/eslint-config": "^2.4.1",
29+
"@types/react": "^18.2.46",
30+
"@types/react-dom": "^18.2.18",
31+
"eslint": "^8.56.0",
3232
"typescript": "^5.3.3"
3333
},
3434
"engines": {

examples/vite/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# vite-example
22

3+
## 0.0.13
4+
5+
### Patch Changes
6+
7+
- Updated dependencies
8+
9+
10+
311
## 0.0.12
412

513
### Patch Changes

examples/vite/package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vite-example",
33
"private": true,
4-
"version": "0.0.12",
4+
"version": "0.0.13",
55
"type": "module",
66
"scripts": {
77
"dev": "vite --port 3001",
@@ -13,20 +13,20 @@
1313
"@mayank1513/fork-me": "^2.0.0",
1414
"react": "^18.2.0",
1515
"react-dom": "^18.2.0",
16-
"react-router-dom": "^6.20.1",
16+
"react-router-dom": "^6.21.1",
1717
"react18-themes": "workspace:*",
1818
"shared-ui": "workspace:*"
1919
},
2020
"devDependencies": {
21-
"@types/react": "^18.2.42",
22-
"@types/react-dom": "^18.2.17",
23-
"@typescript-eslint/eslint-plugin": "^6.13.2",
24-
"@typescript-eslint/parser": "^6.13.2",
21+
"@types/react": "^18.2.46",
22+
"@types/react-dom": "^18.2.18",
23+
"@typescript-eslint/eslint-plugin": "^6.16.0",
24+
"@typescript-eslint/parser": "^6.16.0",
2525
"@vitejs/plugin-react-swc": "^3.5.0",
26-
"eslint": "^8.55.0",
26+
"eslint": "^8.56.0",
2727
"eslint-plugin-react-hooks": "^4.6.0",
2828
"eslint-plugin-react-refresh": "^0.4.5",
2929
"typescript": "^5.3.3",
30-
"vite": "^5.0.7"
30+
"vite": "^5.0.10"
3131
}
3232
}

lib/react18-themes/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# react18-themes
22

3+
## 2.3.0
4+
5+
### Minor Changes
6+
7+
- Add storage prop - No cookies by default!
8+
39
## 2.2.0
410

511
### Minor Changes

lib/react18-themes/package.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "react18-themes",
33
"author": "Mayank Kumar Chaudhari <https://mayank-chaudhari.vercel.app>",
44
"private": false,
5-
"version": "2.2.0",
5+
"version": "2.3.0",
66
"description": "Unleash the Power of React Server Components! Use multiple themes on your site with confidence, without losing any advantages of React Server Components.",
77
"main": "./index.ts",
88
"types": "./index.ts",
@@ -25,33 +25,33 @@
2525
"gen": "turbo gen react-component"
2626
},
2727
"devDependencies": {
28-
"@remix-run/node": "^2.3.1",
29-
"@remix-run/react": "^2.3.1",
30-
"@remix-run/testing": "^2.3.1",
28+
"@remix-run/node": "^2.4.1",
29+
"@remix-run/react": "^2.4.1",
30+
"@remix-run/testing": "^2.4.1",
3131
"@testing-library/react": "^14.1.2",
32-
"@turbo/gen": "^1.11.1",
33-
"@types/node": "^20.10.4",
34-
"@types/react": "^18.2.42",
35-
"@types/react-dom": "^18.2.17",
32+
"@turbo/gen": "^1.11.2",
33+
"@types/node": "^20.10.6",
34+
"@types/react": "^18.2.46",
35+
"@types/react-dom": "^18.2.18",
3636
"@vitejs/plugin-react": "^4.2.1",
37-
"@vitest/coverage-v8": "^1.0.2",
37+
"@vitest/coverage-v8": "^1.1.1",
3838
"esbuild-plugin-react18": "^0.0.6",
39-
"eslint": "^8.55.0",
39+
"eslint": "^8.56.0",
4040
"eslint-config-custom": "workspace:*",
4141
"jsdom": "^23.0.1",
4242
"next": "^14.0.4",
4343
"octokit": "^3.1.2",
4444
"react": "^18.2.0",
4545
"tsconfig": "workspace:*",
4646
"tsup": "^8.0.1",
47-
"turbo": "^1.11.1",
48-
"typedoc": "^0.25.4",
47+
"turbo": "^1.11.2",
48+
"typedoc": "^0.25.5",
4949
"typescript": "5.3.3",
50-
"vite-tsconfig-paths": "^4.2.2",
51-
"vitest": "^1.0.2"
50+
"vite-tsconfig-paths": "^4.2.3",
51+
"vitest": "^1.1.1"
5252
},
5353
"dependencies": {
54-
"persist-and-sync": "^1.1.1",
54+
"persist-and-sync": "^1.2.0",
5555
"zustand": "^4.4.7"
5656
},
5757
"peerDependencies": {

lib/react18-themes/src/client/theme-switcher/theme-switcher.tsx

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,18 @@ import { useEffect } from "react";
33
import type { ColorSchemeType } from "../../store";
44
import { useTheme } from "../../store";
55
import { resolveTheme } from "../../utils";
6+
import { StorageType } from "persist-and-sync";
67

78
export interface ThemeSwitcherProps {
89
forcedTheme?: string;
910
forcedColorScheme?: ColorSchemeType;
1011
targetSelector?: string;
1112
themeTransition?: string;
12-
}
13-
14-
export function ThemeSwitcher(props: ThemeSwitcherProps) {
15-
useThemeSwitcher(props);
16-
return null;
17-
}
18-
19-
export function useThemeSwitcher(props: ThemeSwitcherProps) {
20-
const depArray = useTheme(state => [
21-
state.theme,
22-
state.darkTheme,
23-
state.lightTheme,
24-
state.colorSchemePref,
25-
state.forcedColorScheme,
26-
state.forcedTheme,
27-
]);
28-
29-
useEffect(() => {
30-
const themeState = useTheme.getState();
31-
const media = matchMedia("(prefers-color-scheme: dark)");
32-
const updateTheme = () => {
33-
const restoreTransitions = disableAnimation(props.themeTransition);
34-
35-
const resolvedData = resolveTheme(media.matches, themeState, props);
36-
themeState.setResolved(resolvedData);
37-
updateDOM(resolvedData, media.matches, props.targetSelector);
38-
39-
restoreTransitions();
40-
};
41-
42-
media.addEventListener("change", updateTheme);
43-
updateTheme();
44-
return () => {
45-
media.removeEventListener("change", updateTheme);
46-
};
47-
}, [props, ...depArray]);
13+
/**
14+
* defaultValue `"localStorage"`
15+
* set storage to `cookies` (recommended when using server components) or `sessionsStorage` when using only client side or when you must avoid using cookies
16+
*/
17+
storage?: StorageType;
4818
}
4919

5020
export interface DataProps {
@@ -99,3 +69,48 @@ const disableAnimation = (themeTransition = "none") => {
9969
}, 1);
10070
};
10171
};
72+
73+
/**
74+
* You can use this hook in place of `<ThemeSwitcher />` component.
75+
* Please note that you need to add "use client" on top of the component in which you are using this hook.
76+
*/
77+
export function useThemeSwitcher(props: ThemeSwitcherProps) {
78+
const [setStorage, ...depArray] = useTheme(state => [
79+
state.setStorage,
80+
state.theme,
81+
state.darkTheme,
82+
state.lightTheme,
83+
state.colorSchemePref,
84+
state.forcedColorScheme,
85+
state.forcedTheme,
86+
]);
87+
88+
useEffect(() => {
89+
setStorage(props.storage ?? "localStorage");
90+
}, [props.storage]);
91+
92+
useEffect(() => {
93+
const themeState = useTheme.getState();
94+
const media = matchMedia("(prefers-color-scheme: dark)");
95+
const updateTheme = () => {
96+
const restoreTransitions = disableAnimation(props.themeTransition);
97+
98+
const resolvedData = resolveTheme(media.matches, themeState, props);
99+
themeState.setResolved(resolvedData);
100+
updateDOM(resolvedData, media.matches, props.targetSelector);
101+
102+
restoreTransitions();
103+
};
104+
105+
media.addEventListener("change", updateTheme);
106+
updateTheme();
107+
return () => {
108+
media.removeEventListener("change", updateTheme);
109+
};
110+
}, [props, ...depArray]);
111+
}
112+
113+
export function ThemeSwitcher(props: ThemeSwitcherProps) {
114+
useThemeSwitcher(props);
115+
return null;
116+
}

0 commit comments

Comments
 (0)