diff --git a/.envrc b/.envrc
index 8106bf9..117c726 100644
--- a/.envrc
+++ b/.envrc
@@ -1,13 +1,17 @@
-if ! has nix_direnv_version || ! nix_direnv_version 2.2.1; then
- source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.2.1/direnvrc" "sha256-zelF0vLbEl5uaqrfIzbgNzJWGmLzCmYAkInj/LNxvKs="
+#!/usr/bin/env bash
+
+if ! has nix_direnv_version || ! nix_direnv_version 3.1.0; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.0/direnvrc" "sha256-yMJ2OVMzrFaDPn7q8nCBZFRYpL/f0RcHzhmw/i6btJM="
fi
+export DEVENV_IN_DIRENV_SHELL=true
+
watch_file flake.nix
watch_file flake.lock
-DEVENV_ROOT_FILE="$(mktemp)"
-printf %s "$PWD" > "$DEVENV_ROOT_FILE"
-if ! use flake . --override-input devenv-root "file+file://$DEVENV_ROOT_FILE"
-then
+mkdir -p "$PWD/.devenv"
+DEVENV_ROOT_FILE="$PWD/.devenv/root"
+printf %s "$PWD" >"$DEVENV_ROOT_FILE"
+if ! use flake . --override-input devenv-root "file+file://$DEVENV_ROOT_FILE"; then
echo "devenv could not be built. The devenv environment was not loaded. Make the necessary changes to devenv.nix and hit enter to try again." >&2
fi
diff --git a/.gitignore b/.gitignore
index d2fcaf2..acf6b4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,8 +8,10 @@ node_modules
# testing
/coverage
-# production
-/build
+# React Router
+.react-router/
+/build/
+
# misc
.DS_Store
diff --git a/biome.json b/biome.json
index 7cdcf69..1498ed4 100644
--- a/biome.json
+++ b/biome.json
@@ -1,7 +1,13 @@
{
- "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
- "organizeImports": {
- "enabled": true
+ "$schema": "https://biomejs.dev/schemas/2.3.6/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignoreUnknown": false,
+ "includes": ["**", "!**/.react-router"]
},
"formatter": {
"enabled": true,
@@ -20,9 +26,17 @@
}
}
},
- "vcs": {
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double"
+ }
+ },
+ "assist": {
"enabled": true,
- "clientKind": "git",
- "useIgnoreFile": true
+ "actions": {
+ "source": {
+ "organizeImports": "on"
+ }
+ }
}
}
diff --git a/bun.lockb b/bun.lockb
index d0d030b..5faa1c3 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/flake.nix b/flake.nix
index 8d7fa81..2525c5e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -6,8 +6,9 @@
url = "file+file:///dev/null";
flake = false;
};
+ nixpkgs.url = "github:cachix/devenv-nixpkgs/rolling";
flake-parts.url = "github:hercules-ci/flake-parts";
- nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
+ flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
devenv.url = "github:cachix/devenv";
nix2container.url = "github:nlewo/nix2container";
nix2container.inputs.nixpkgs.follows = "nixpkgs";
@@ -19,54 +20,40 @@
extra-substituters = "https://devenv.cachix.org";
};
- outputs = inputs @ {
- flake-parts,
- devenv-root,
- ...
- }:
- flake-parts.lib.mkFlake {inherit inputs;} {
+ outputs =
+ inputs@{ flake-parts, devenv-root, ... }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
- systems = ["x86_64-linux" "i686-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
+ systems = [
+ "x86_64-linux"
+ "i686-linux"
+ "x86_64-darwin"
+ "aarch64-linux"
+ "aarch64-darwin"
+ ];
- perSystem = {
- #config,
- #self',
- ##inputs',
- pkgs,
- #system,
- ...
- }: let
- in {
- devenv.shells.default = {
- devenv.root = let
- devenvRootFileContent = builtins.readFile devenv-root.outPath;
- in
- pkgs.lib.mkIf (devenvRootFileContent != "") devenvRootFileContent;
+ perSystem =
+ {
+ config,
+ self',
+ inputs',
+ pkgs,
+ system,
+ ...
+ }:
+ {
+ devenv.shells.default = {
+ name = "my-project";
+ imports = [ ];
- name = "TODO-template-name";
-
- packages = [
- pkgs.biome
- ];
-
- processes = {
- #api-dev.exec = "cd packages/api && bun dev";
- #web-dev.exec = "cd packages/web && bun dev";
- };
-
- languages.javascript = {
- enable = true;
- package = pkgs.nodejs_22;
-
- bun.enable = true;
- npm.enable = false;
- pnpm.enable = false;
+ languages.javascript = {
+ enable = true;
+ bun.enable = true;
+ };
};
};
- };
- flake = {
- };
+ flake = { };
};
}
diff --git a/packages/web/app/app.css b/packages/web/app/app.css
new file mode 100644
index 0000000..71277f5
--- /dev/null
+++ b/packages/web/app/app.css
@@ -0,0 +1,4 @@
+html,
+body {
+ background-color: teal;
+}
diff --git a/packages/web/app/root.tsx b/packages/web/app/root.tsx
new file mode 100644
index 0000000..9fc6636
--- /dev/null
+++ b/packages/web/app/root.tsx
@@ -0,0 +1,75 @@
+import {
+ isRouteErrorResponse,
+ Links,
+ Meta,
+ Outlet,
+ Scripts,
+ ScrollRestoration,
+} from "react-router";
+
+import type { Route } from "./+types/root";
+import "./app.css";
+
+export const links: Route.LinksFunction = () => [
+ { rel: "preconnect", href: "https://fonts.googleapis.com" },
+ {
+ rel: "preconnect",
+ href: "https://fonts.gstatic.com",
+ crossOrigin: "anonymous",
+ },
+ {
+ rel: "stylesheet",
+ href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
+ },
+];
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+
+export default function App() {
+ return ;
+}
+
+export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
+ let message = "Oops!";
+ let details = "An unexpected error occurred.";
+ let stack: string | undefined;
+
+ if (isRouteErrorResponse(error)) {
+ message = error.status === 404 ? "404" : "Error";
+ details =
+ error.status === 404
+ ? "The requested page could not be found."
+ : error.statusText || details;
+ } else if (import.meta.env.DEV && error && error instanceof Error) {
+ details = error.message;
+ stack = error.stack;
+ }
+
+ return (
+
+ {message}
+ {details}
+ {stack && (
+
+ {stack}
+
+ )}
+
+ );
+}
diff --git a/packages/web/app/routes.ts b/packages/web/app/routes.ts
new file mode 100644
index 0000000..102b402
--- /dev/null
+++ b/packages/web/app/routes.ts
@@ -0,0 +1,3 @@
+import { type RouteConfig, index } from "@react-router/dev/routes";
+
+export default [index("routes/home.tsx")] satisfies RouteConfig;
diff --git a/packages/web/app/routes/home.tsx b/packages/web/app/routes/home.tsx
new file mode 100644
index 0000000..28efe48
--- /dev/null
+++ b/packages/web/app/routes/home.tsx
@@ -0,0 +1,13 @@
+import type { Route } from "./+types/home";
+import { Welcome } from "../welcome/welcome";
+
+export function meta(_args: Route.MetaArgs) {
+ return [
+ { title: "New React Router App" },
+ { name: "description", content: "Welcome to React Router!" },
+ ];
+}
+
+export default function Home() {
+ return ;
+}
diff --git a/packages/web/app/welcome/logo-dark.svg b/packages/web/app/welcome/logo-dark.svg
new file mode 100644
index 0000000..dd82028
--- /dev/null
+++ b/packages/web/app/welcome/logo-dark.svg
@@ -0,0 +1,23 @@
+
diff --git a/packages/web/app/welcome/logo-light.svg b/packages/web/app/welcome/logo-light.svg
new file mode 100644
index 0000000..7328492
--- /dev/null
+++ b/packages/web/app/welcome/logo-light.svg
@@ -0,0 +1,23 @@
+
diff --git a/packages/web/app/welcome/welcome.tsx b/packages/web/app/welcome/welcome.tsx
new file mode 100644
index 0000000..f4c185f
--- /dev/null
+++ b/packages/web/app/welcome/welcome.tsx
@@ -0,0 +1,91 @@
+import logoDark from "./logo-dark.svg";
+import logoLight from "./logo-light.svg";
+
+export function Welcome() {
+ return (
+
+
+
+
+

+

+
+
+
+
+
+
+
+ );
+}
+
+const resources = [
+ {
+ href: "https://reactrouter.com/docs",
+ text: "React Router Docs",
+ icon: (
+
+ ),
+ },
+ {
+ href: "https://rmx.as/discord",
+ text: "Join Discord",
+ icon: (
+
+ ),
+ },
+];
diff --git a/packages/web/index.html b/packages/web/index.html
deleted file mode 100644
index 2ce0cce..0000000
--- a/packages/web/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
- Template
-
-
-
-
-
-
diff --git a/packages/web/package.json b/packages/web/package.json
index c5bdd78..d6e3b1b 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -4,33 +4,36 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "bunx --bun vite",
- "build": "tsc -b && vite build",
- "lint": "biome lint . && stylelint **/*.css",
- "preview": "vite preview",
+ "build": "react-router build",
+ "dev": "react-router dev",
+ "start": "react-router-serve ./build/server/index.js",
+ "typecheck": "react-router typegen && tsc",
"test": "vitest"
},
"dependencies": {
- "effect": "=3.14.5",
- "react": "=19.1.0",
- "react-dom": "=19.1.0",
+ "@react-router/node": "7.10.1",
+ "@react-router/serve": "7.10.1",
+ "isbot": "^5.1.31",
+ "react": "^19.2.3",
+ "react-dom": "^19.2.3",
+ "react-router": "7.10.1",
"react-hook-form": "7.55.0",
- "react-router-dom": "7.4.1",
"vite-plugin-top-level-await": "1.5.0"
},
"devDependencies": {
- "@biomejs/biome": "1.9.4",
- "@types/lodash": "4.17.16",
- "@types/react": "19.1.0",
- "@types/react-dom": "19.1.1",
- "@vitejs/plugin-react": "4.3.4",
- "meow": "13.2.0",
- "stylelint": "16.17.0",
- "stylelint-config-standard": "37.0.0",
- "typescript": "5.8.2",
- "typescript-eslint": "8.29.0",
- "vite": "6.2.4",
- "vite-plugin-checker": "0.9.1",
- "vitest": "3.1.1"
+ "@react-router/dev": "7.10.1",
+ "@tailwindcss/vite": "^4.1.13",
+ "@types/node": "^22",
+ "@types/react": "^19.2.7",
+ "@types/react-dom": "^19.2.3",
+ "tailwindcss": "^4.1.13",
+ "typescript": "^5.9.2",
+ "vite": "^7.1.7",
+ "vite-tsconfig-paths": "^5.1.4",
+ "@biomejs/biome": "2.3.6",
+ "stylelint": "16.26.1",
+ "stylelint-config-standard": "39.0.1",
+ "vite-plugin-checker": "0.12.0",
+ "vitest": "4.0.16"
}
}
diff --git a/packages/web/public/favicon.ico b/packages/web/public/favicon.ico
index a11777c..5dbdfcd 100644
Binary files a/packages/web/public/favicon.ico and b/packages/web/public/favicon.ico differ
diff --git a/packages/web/public/logo192.png b/packages/web/public/logo192.png
deleted file mode 100644
index fc44b0a..0000000
Binary files a/packages/web/public/logo192.png and /dev/null differ
diff --git a/packages/web/public/logo512.png b/packages/web/public/logo512.png
deleted file mode 100644
index a4e47a6..0000000
Binary files a/packages/web/public/logo512.png and /dev/null differ
diff --git a/packages/web/public/manifest.json b/packages/web/public/manifest.json
deleted file mode 100644
index 080d6c7..0000000
--- a/packages/web/public/manifest.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "short_name": "React App",
- "name": "Create React App Sample",
- "icons": [
- {
- "src": "favicon.ico",
- "sizes": "64x64 32x32 24x24 16x16",
- "type": "image/x-icon"
- },
- {
- "src": "logo192.png",
- "type": "image/png",
- "sizes": "192x192"
- },
- {
- "src": "logo512.png",
- "type": "image/png",
- "sizes": "512x512"
- }
- ],
- "start_url": ".",
- "display": "standalone",
- "theme_color": "#000000",
- "background_color": "#ffffff"
-}
diff --git a/packages/web/public/robots.txt b/packages/web/public/robots.txt
deleted file mode 100644
index e9e57dc..0000000
--- a/packages/web/public/robots.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-# https://www.robotstxt.org/robotstxt.html
-User-agent: *
-Disallow:
diff --git a/packages/web/react-router.config.ts b/packages/web/react-router.config.ts
new file mode 100644
index 0000000..6ff16f9
--- /dev/null
+++ b/packages/web/react-router.config.ts
@@ -0,0 +1,7 @@
+import type { Config } from "@react-router/dev/config";
+
+export default {
+ // Config options...
+ // Server-side render by default, to enable SPA mode set this to `false`
+ ssr: true,
+} satisfies Config;
diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx
deleted file mode 100644
index 4ed09e9..0000000
--- a/packages/web/src/App.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import {
- createBrowserRouter,
- Navigate,
- Outlet,
- RouterProvider,
-} from "react-router-dom";
-
-const router = createBrowserRouter([
- {
- path: "/",
- element: ,
- children: [
- {
- path: "/",
- element: ,
- },
- {
- path: "test",
- element: Test
,
- },
- ],
- },
-]);
-
-function App() {
- return ;
-}
-
-export default App;
diff --git a/packages/web/src/index.css b/packages/web/src/index.css
deleted file mode 100644
index be4b9d3..0000000
--- a/packages/web/src/index.css
+++ /dev/null
@@ -1,5 +0,0 @@
-html,
-body {
- margin: 0;
- padding: 0;
-}
diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx
deleted file mode 100644
index bb311b2..0000000
--- a/packages/web/src/main.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
-import App from "./App.tsx";
-import "./index.css";
-
-const root = document.getElementById("root");
-
-if (root != null) {
- createRoot(root).render(
-
-
- ,
- );
-} else {
- console.error("No #root element found");
-}
diff --git a/packages/web/src/requireContext.ts b/packages/web/src/requireContext.ts
deleted file mode 100644
index e471762..0000000
--- a/packages/web/src/requireContext.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { useContext } from "react";
-
-export function useRequiredContext(
- c: React.Context,
- error: string,
-): T {
- const ctx = useContext(c);
- if (ctx == null) {
- throw new Error(error);
- }
- return ctx;
-}
diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json
index 1673d04..ca6bcbf 100644
--- a/packages/web/tsconfig.json
+++ b/packages/web/tsconfig.json
@@ -1,5 +1,17 @@
{
"files": [],
"extends": "../../tsconfig.base.json",
- "include": ["src"]
+ "include": [
+ "**/*",
+ "**/.server/**/*",
+ "**/.client/**/*",
+ ".react-router/types/**/*"
+ ],
+ "compilerOptions": {
+ "rootDirs": [".", "./.react-router/types"],
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./app/*"]
+ },
+ }
}
diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts
index 707457b..2c2facc 100644
--- a/packages/web/vite.config.ts
+++ b/packages/web/vite.config.ts
@@ -1,6 +1,7 @@
+import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
import checker from "vite-plugin-checker";
+import tsconfigPaths from "vite-tsconfig-paths";
// https://vitejs.dev/config/
export default defineConfig({
@@ -8,9 +9,10 @@ export default defineConfig({
checker({
biome: { command: "check", dev: { command: "lint" } },
typescript: true,
- stylelint: { lintCommand: "stylelint ./src/**/*.css" },
+ stylelint: { lintCommand: "stylelint ./app/**/*.css" },
}),
- react(),
+ reactRouter(),
+ tsconfigPaths(),
],
server: {
fs: {
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 37782bc..30bda58 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -3,7 +3,7 @@
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
- "types": ["vitest/importMeta"],
+ "types": ["node", "vite/client", "vitest/importMeta"],
"module": "ESNext",
"skipLibCheck": true,
@@ -16,6 +16,7 @@
"strict": true,
+ "esModuleInterop": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"exactOptionalPropertyTypes": true,
@@ -26,6 +27,8 @@
"noUncheckedIndexedAccess": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
+ "verbatimModuleSyntax": true,
+ "resolveJsonModule": true,
"checkJs": true,
"verbatimModuleSyntax": true,