{"id":13720091,"url":"https://github.com/abereghici/remix-themes","last_synced_at":"2026-02-06T01:34:34.144Z","repository":{"id":37736766,"uuid":"439638854","full_name":"abereghici/remix-themes","owner":"abereghici","description":"An abstraction for themes in your Remix app.","archived":false,"fork":false,"pushed_at":"2025-01-14T16:46:52.000Z","size":844,"stargazers_count":148,"open_issues_count":6,"forks_count":21,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-16T17:59:42.799Z","etag":null,"topics":["dark-mode","dark-theme","javascript","react","remix","remix-run","theme","themes","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/remix-themes","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abereghici.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-18T14:41:51.000Z","updated_at":"2025-04-13T10:48:41.000Z","dependencies_parsed_at":"2023-09-22T19:52:09.665Z","dependency_job_id":"85a0de7a-33f0-45db-a4b6-e8ad13857e70","html_url":"https://github.com/abereghici/remix-themes","commit_stats":{"total_commits":41,"total_committers":5,"mean_commits":8.2,"dds":"0.14634146341463417","last_synced_commit":"0ad652e85f319f4f3f5576463168f8c6abe75129"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abereghici%2Fremix-themes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abereghici%2Fremix-themes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abereghici%2Fremix-themes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abereghici%2Fremix-themes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abereghici","download_url":"https://codeload.github.com/abereghici/remix-themes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252876297,"owners_count":21818158,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dark-mode","dark-theme","javascript","react","remix","remix-run","theme","themes","typescript"],"created_at":"2024-08-03T01:00:59.655Z","updated_at":"2026-02-06T01:34:29.097Z","avatar_url":"https://github.com/abereghici.png","language":"TypeScript","funding_links":[],"categories":["Utility","Packages","TypeScript"],"sub_categories":[],"readme":"# Remix Themes\n\n### An abstraction for themes in your [React router](https://reactrouter.com/) / [Remix](https://remix.run/) app.\n\n## Features\n\n- ✅ Perfect dark mode in a few lines of code\n- ✅ System setting with prefers-color-scheme\n- ✅ No flash on load\n- ✅ Disable flashing when changing themes\n- ✅ Class or data attribute selector\n- ✅ Sync theme across tabs and windows\n\nCheck out the\n[Example](https://github.com/abereghici/remix-themes/tree/main/test-apps/react-router-app)\nto see it in action.\n\nIf you are using Remix.run you can use v1.6.1 of this library or lower, v2 onwards is only react-router v7 compatible.\n\n## Install\n\n```bash\n$ npm install remix-themes\n# or\n$ yarn add remix-themes\n```\n\n## Getting Started\n\n### Create your session storage and create a themeSessionResolver\n\n```ts\n// sessions.server.tsx\n\nimport { createThemeSessionResolver } from \"remix-themes\";\nimport { createCookieSessionStorage } from \"react-router\";\n\nconst sessionStorage = createCookieSessionStorage({\n  cookie: {\n    name: \"__remix-themes\",\n    // domain: 'remix.run',\n    path: \"/\",\n    httpOnly: true,\n    sameSite: \"lax\",\n    secrets: [\"s3cr3t\"],\n    // secure: true,\n  },\n});\n\nexport const themeSessionResolver = createThemeSessionResolver(sessionStorage);\n```\n\nNote: make sure you have `domain` and `secure` parameters set only for your\nproduction environment. Otherwise, Safari won't store the cookie if you set\nthese parameters on localhost.\n\n### Setup Remix Themes\n\n```ts\n// root.tsx\n\nimport {\n  ThemeProvider,\n  useTheme,\n  PreventFlashOnWrongTheme,\n} from \"remix-themes\";\nimport { themeSessionResolver } from \"./sessions.server\";\n\n// Return the theme from the session storage using the loader\nexport const loader: LoaderFunction = async ({ request }) =\u003e {\n  const { getTheme } = await themeSessionResolver(request);\n  return {\n    theme: getTheme(),\n  };\n};\n\n// Wrap your app with ThemeProvider.\n// `specifiedTheme` is the stored theme in the session storage.\n// `themeAction` is the action name that's used to change the theme in the session storage.\nexport default function AppWithProviders() {\n  const data = useLoaderData();\n  return (\n    \u003cThemeProvider specifiedTheme={data.theme} themeAction=\"/action/set-theme\"\u003e\n      \u003cApp /\u003e\n    \u003c/ThemeProvider\u003e\n  );\n}\n\n// Use the theme in your app.\n// If the theme is missing in session storage, PreventFlashOnWrongTheme will get\n// the browser theme before hydration and will prevent a flash in browser.\n// The client code runs conditionally, it won't be rendered if we have a theme in session storage.\nfunction App() {\n  const data = useLoaderData();\n  const [theme] = useTheme();\n  return (\n    \u003chtml lang=\"en\" data-theme={theme ?? \"\"}\u003e\n      \u003chead\u003e\n        \u003cmeta charSet=\"utf-8\" /\u003e\n        \u003cmeta name=\"viewport\" content=\"width=device-width,initial-scale=1\" /\u003e\n        \u003cMeta /\u003e\n        \u003cPreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} /\u003e\n        \u003cLinks /\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cOutlet /\u003e\n        \u003cScrollRestoration /\u003e\n        \u003cScripts /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n}\n```\n\n#### Add the action route\n\nCreate a file in `/routes/action/set-theme.ts` or `/routes/action.set-theme.ts`\nwhen using\n[Route File Naming v2](https://remix.run/docs/en/1.19.3/file-conventions/route-files-v2#route-file-naming-v2)\nwith the content below. Ensure that you pass the filename to the `ThemeProvider`\ncomponent.\n\n\u003e Note: You can name the action route whatever you want. Just make sure you pass\n\u003e the correct action name to the `ThemeProvider` component. Make sure to use\n\u003e absolute path when using nested routing.\n\nThis route is used to store the preferred theme in the session storage when\nthe user changes it.\n\n```ts\nimport { createThemeAction } from \"remix-themes\";\nimport { themeSessionResolver } from \"./sessions.server\";\n\nexport const action = createThemeAction(themeSessionResolver);\n```\n\n## API\n\nLet's dig into the details.\n\n### ThemeProvider\n\n- `specifiedTheme`: The theme from the session storage.\n- `themeAction`: The action name used to change the theme in the session\n  storage.\n- `disableTransitionOnThemeChange`: Disable CSS transitions on theme change to\n  prevent the flashing effect.\n\n### useTheme\n\nuseTheme takes no parameters but returns:\n\n- `theme`: Active theme name\n- `setTheme`: Function to set the theme. If the theme is set to `null`, the\n  system theme will be used and `definedBy` property in the `metadata` object\n  will be set to `SYSTEM`.\n- `metadata`: An object which contains the following properties:\n  - `definedBy`: The theme source. It can be `USER` or `SYSTEM`. Useful to\n    detect if the theme was set by the user or by the system.\n\n### createThemeSessionResolver\n\n`createThemeSessionResolver` function takes a cookie session storage and returns\n\n- `resolver`: A function that takes a request and returns an object with the\n  following properties:\n  - `getTheme`: A function that returns the theme from the session storage.\n  - `setTheme`: A function that takes a theme name and sets it in the session\n    storage.\n  - `commit`: A function that commits the session storage (Stores all data in\n    the session and returns the Set-Cookie header to use in the HTTP response.)\n\n### PreventFlashOnWrongTheme\n\nOn the server, \"theme\" might be `null` so `PreventFlashOnWrongTheme` ensures\nthat this is correct before hydration. If the theme is null on the server, this\ncomponent will set the browser theme on the `html` element in a `data-theme`\nattribute if exists, otherwise it will be set to a `class` attribute. If both\n`data-theme` and `class` are set, the `data-theme` will be used.\n\n- `ssrTheme`: boolean value that indicates if we have a theme in the session\n  storage.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabereghici%2Fremix-themes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabereghici%2Fremix-themes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabereghici%2Fremix-themes/lists"}