{"id":21923826,"url":"https://github.com/josuerhea/remix-themes","last_synced_at":"2025-06-11T03:10:22.865Z","repository":{"id":187084117,"uuid":"676170050","full_name":"JosueRhea/remix-themes","owner":"JosueRhea","description":"Dark and light mode with SSR for Remix.run","archived":false,"fork":false,"pushed_at":"2023-08-09T21:59:14.000Z","size":500,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-04T01:37:47.492Z","etag":null,"topics":["dark-mode","remix","remix-run","theme-ui","themes"],"latest_commit_sha":null,"homepage":"","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/JosueRhea.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2023-08-08T15:29:57.000Z","updated_at":"2023-08-09T21:49:44.000Z","dependencies_parsed_at":"2025-01-27T10:44:16.569Z","dependency_job_id":"c0318df8-2bef-4fdd-a41b-a3cc3aded052","html_url":"https://github.com/JosueRhea/remix-themes","commit_stats":null,"previous_names":["josuerhea/remix-themes"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JosueRhea%2Fremix-themes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JosueRhea%2Fremix-themes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JosueRhea%2Fremix-themes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JosueRhea%2Fremix-themes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JosueRhea","download_url":"https://codeload.github.com/JosueRhea/remix-themes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244946076,"owners_count":20536372,"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","remix","remix-run","theme-ui","themes"],"created_at":"2024-11-28T21:12:53.526Z","updated_at":"2025-03-22T11:19:24.048Z","avatar_url":"https://github.com/JosueRhea.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Remix themes\n\nDark and light mode with SSR\n\n### Installation\n\n```bash\npnpm i @josuerhea/remix-themes\n#or\nnpm i @josuerhea/remix-themes\n```\n\n### Usage\n\nFirt create `app/theme.server.ts` to setup the session storage\n```ts\n// app/theme.server.ts\n\nimport { createCookieSessionStorage } from \"@remix-run/node\";\nimport { Theme, isTheme } from \"@josuerhea/remix-themes\";\n\nconst themeStorage = createCookieSessionStorage({\n  cookie: {\n    name: \"your-theme-name\",\n    secure: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    httpOnly: true,\n    secrets: [\"your-secret\"],\n  },\n});\n\nasync function getThemeSession(request: Request) {\n  const session = await themeStorage.getSession(request.headers.get(\"Cookie\"));\n  return {\n    getTheme: () =\u003e {\n      const themeValue = session.get(\"theme\");\n      return isTheme(themeValue) ? themeValue : Theme.DARK;\n    },\n    setTheme: (theme: Theme) =\u003e session.set(\"theme\", theme),\n    commit: () =\u003e\n      // no theme for you on my 100th birthday! 😂\n      themeStorage.commitSession(session, { expires: new Date(\"2088-10-18\") }),\n  };\n}\n\nexport { getThemeSession };\n```\n\nUpdate the `app/root.tsx` file. You'll ned a extra component that look like this.\n\n```tsx\n// app/root.tsx\nimport type { LinksFunction, LoaderArgs, SerializeFrom } from \"@remix-run/node\";\nimport { NonFlashOfWrongThemeEls, ThemeProvider, useTheme } from \"@josuerhea/remix-themes\";\nimport { getThemeSession } from \"~/theme.server\";\nimport stylesheet from \"~/tailwind.css\";\nimport { useLoaderData } from \"@remix-run/react\";\n\nexport const links: LinksFunction = () =\u003e [  \n  { rel: \"stylesheet\", href: stylesheet },\n];\n\nexport async function loader({ request }: LoaderArgs) {\n  const theme = await getThemeSession(request);\n\n  return { theme: theme.getTheme() };\n}\n\ntype LoaderData = SerializeFrom\u003ctypeof loader\u003e;\n\nexport function App() {\n  const data = useLoaderData\u003cLoaderData\u003e();\n  const [theme] = useTheme();\n\n  return (\n    \u003chtml lang=\"en\" className={theme ?? \"\"}\u003e\n      \u003chead\u003e\n        {/* ... */}\n        \u003cNonFlashOfWrongThemeEls ssrTheme={Boolean(data.theme)} /\u003e\n        {/* ... */}\n      \u003c/head\u003e\n      \u003cbody\u003e\n        {/* .... */}\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n}\n\nexport default function AppWithProviders() {\n  const data = useLoaderData\u003cLoaderData\u003e();\n  \n  return (\n    \u003cThemeProvider specifiedTheme={data.theme}\u003e\n      \u003cApp /\u003e\n    \u003c/ThemeProvider\u003e\n  );\n}\n```\n\nCreate a new route in `app/routes/action.set-theme.tsx` that will contain the actions that saves the session storage.\n\n```tsx\n// app/routes/action.set-theme.tsx\n\nimport { json, type ActionFunction, redirect } from \"@remix-run/node\";\nimport { isTheme } from \"@josuerhea/remix-themes\";\nimport { getThemeSession } from \"~/theme.server\";\n\nexport const action: ActionFunction = async ({ request }) =\u003e {\n  const themeSession = await getThemeSession(request);\n  const requestText = await request.text();\n  const form = new URLSearchParams(requestText);\n  const theme = form.get(\"theme\");\n  if (!isTheme(theme)) {\n    return json({\n      success: false,\n      message: `theme value of ${theme} is not a valid theme.`,\n    });\n  }\n\n  themeSession.setTheme(theme);\n  return json(\n    { success: true },\n    {\n      headers: { \"Set-Cookie\": await themeSession.commit() },\n    },\n  );\n};\n\nexport const loader = () =\u003e redirect(\"/\", { status: 404 });\n```\n\n### useTheme\nNow you can use the hook to see your current theme or change the theme.\n\n```tsx\n// app/routes/_index.tsx\nimport { Theme, Themed, useTheme } from \"@josuerhea/remix-themes\";\n\nexport default function Page() {\n  const [theme, setTheme] = useTheme();\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003e\n        Active theme: \u003cspan className=\"text-red-500\"\u003e{theme ?? \"\"}\u003c/span\u003e\n      \u003c/p\u003e\n      \u003cselect\n        value={theme ?? \"\"}s\n        onChange={(e) =\u003e setTheme(e.target.value as Theme)}\n      \u003e\n        \u003coption value=\"dark\"\u003eDark\u003c/option\u003e\n        \u003coption value=\"light\"\u003eLight\u003c/option\u003e\n      \u003c/select\u003e\n    \u003c/div\u003e\n  );\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosuerhea%2Fremix-themes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosuerhea%2Fremix-themes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosuerhea%2Fremix-themes/lists"}