From 6604f850c6b5b131adac176bbde91071d46c9635 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:31:21 +0200 Subject: [PATCH 01/24] Better eslint config --- eslint.config.js | 51 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 79a552e..33690a1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -5,13 +5,31 @@ import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; export default tseslint.config( - { ignores: ["dist"] }, { - extends: [js.configs.recommended, ...tseslint.configs.recommended], + ignores: [ + "dist", + "eslint.config.js", + "convex/_generated", + "postcss.config.js", + "tailwind.config.js", + "vite.config.ts", + ], + }, + { + extends: [ + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ], files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, - globals: globals.browser, + globals: { + ...globals.browser, + ...globals.node, + }, + parserOptions: { + project: ["./tsconfig.node.json", "./tsconfig.app.json"], + }, }, plugins: { "react-hooks": reactHooks, @@ -23,6 +41,33 @@ export default tseslint.config( "warn", { allowConstantExport: true }, ], + // All of these overrides ease getting into + // TypeScript, and can be removed for stricter + // linting down the line. + + // Only warn on unused variables, and ignore variables starting with `_` + "@typescript-eslint/no-unused-vars": [ + "warn", + { varsIgnorePattern: "^_", argsIgnorePattern: "^_" }, + ], + + // Allow escaping the compiler + "@typescript-eslint/ban-ts-comment": "error", + + // Allow explicit `any`s + "@typescript-eslint/no-explicit-any": "off", + + // START: Allow implicit `any`s + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + // END: Allow implicit `any`s + + // Allow async functions without await + // for consistency (esp. Convex `handler`s) + "@typescript-eslint/require-await": "off", }, }, ); From a0079ac0fd3885e2dc67101a0331188b77babdc8 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:31:48 +0200 Subject: [PATCH 02/24] More robust lint npm script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04d7914..2396e95 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "dev:backend": "convex dev", "predev": "convex dev --until-success && convex dashboard", "build": "tsc -b && vite build", - "lint": "eslint .", + "lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, "dependencies": { From 8f192824bc31c3bf87ac28a43b7466edacb6cd90 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 16:33:52 +0200 Subject: [PATCH 03/24] Erase UI #noui --- src/App.css | 42 --------------------------- src/App.tsx | 36 ++--------------------- src/assets/react.svg | 1 - src/index.css | 68 -------------------------------------------- 4 files changed, 2 insertions(+), 145 deletions(-) delete mode 100644 src/App.css delete mode 100644 src/assets/react.svg diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.tsx b/src/App.tsx index e25f329..edbea37 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,35 +1,3 @@ -import { useState } from "react"; -import reactLogo from "./assets/react.svg"; -import viteLogo from "/vite.svg"; -import "./App.css"; - -function App() { - const [count, setCount] = useState(0); - - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ); +export default function App() { + return "Hello world!"; } - -export default App; diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/index.css b/src/index.css index 08a3ac9..e69de29 100644 --- a/src/index.css +++ b/src/index.css @@ -1,68 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} From bbbd215f3df59769928c14c73d86c8d8e8b507d0 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 4 Jul 2024 13:35:41 +0200 Subject: [PATCH 04/24] Replace favicon --- index.html | 2 +- public/convex.svg | 17 +++++++++++++++++ public/vite.svg | 1 - 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 public/convex.svg delete mode 100644 public/vite.svg diff --git a/index.html b/index.html index e4b78ea..cd4b163 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Vite + React + TS diff --git a/public/convex.svg b/public/convex.svg new file mode 100644 index 0000000..7d70c4d --- /dev/null +++ b/public/convex.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/vite.svg b/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From 3a689bb27a20065eb52caeb09fc8bc2c4cbcfc33 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:35:39 +0200 Subject: [PATCH 05/24] Install tailwind npm install -D tailwindcss @tailwindcss/vite edit vite.config.ts edit src/index.css edit index.html --- index.html | 1 + package.json | 2 ++ src/index.css | 1 + vite.config.ts | 3 ++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index cd4b163..ac66f30 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ + Vite + React + TS diff --git a/package.json b/package.json index 2396e95..1d3b02d 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", + "@tailwindcss/vite": "^4.0.14", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", @@ -28,6 +29,7 @@ "globals": "^15.15.0", "npm-run-all": "^4.1.5", "prettier": "^3.5.3", + "tailwindcss": "^4.0.14", "typescript": "~5.7.2", "typescript-eslint": "^8.24.1", "vite": "^6.2.0" diff --git a/src/index.css b/src/index.css index e69de29..f1d8c73 100644 --- a/src/index.css +++ b/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/vite.config.ts b/vite.config.ts index 0e43ae8..4ff4f8f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(), tailwindcss()], }); From 5e5b208cdf046180acd91580daea2de3fe1c30d4 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:51:31 +0200 Subject: [PATCH 06/24] Configure @ alias for TS --- tsconfig.app.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tsconfig.app.json b/tsconfig.app.json index 358ca9b..8267e1f 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -20,7 +20,12 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": true, + + /* Import paths */ + "paths": { + "@/*": ["./src/*"] + } }, "include": ["src"] } From 13c8e21be476c7d4cbe4aaef917079417008dfea Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:54:02 +0200 Subject: [PATCH 07/24] Add types for node npm i -D @types/node --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1d3b02d..93bc7c0 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "devDependencies": { "@eslint/js": "^9.21.0", "@tailwindcss/vite": "^4.0.14", + "@types/node": "^22.13.10", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", From c59456d8319311f683d13afed2313293c58c5950 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Thu, 16 May 2024 15:55:00 +0200 Subject: [PATCH 08/24] Configure @ alias for Vite --- vite.config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vite.config.ts b/vite.config.ts index 4ff4f8f..506e619 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,8 +1,14 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; +import path from "path"; // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, }); From e3168952c59c946dd6224262602118c4a561adcf Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 15:24:27 -0800 Subject: [PATCH 09/24] Convex add myFunctions.ts + schema.ts --- convex/_generated/api.d.ts | 5 +- convex/_generated/dataModel.d.ts | 34 +++++++------- convex/myFunctions.ts | 78 ++++++++++++++++++++++++++++++++ convex/schema.ts | 12 +++++ 4 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 convex/myFunctions.ts create mode 100644 convex/schema.ts diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 7d71a01..6a067d5 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -13,6 +13,7 @@ import type { FilterApi, FunctionReference, } from "convex/server"; +import type * as myFunctions from "../myFunctions.js"; /** * A utility for referencing Convex functions in your app's API. @@ -22,7 +23,9 @@ import type { * const myFunctionReference = api.myModule.myFunction; * ``` */ -declare const fullApi: ApiFromModules<{}>; +declare const fullApi: ApiFromModules<{ + myFunctions: typeof myFunctions; +}>; export declare const api: FilterApi< typeof fullApi, FunctionReference diff --git a/convex/_generated/dataModel.d.ts b/convex/_generated/dataModel.d.ts index fb12533..8541f31 100644 --- a/convex/_generated/dataModel.d.ts +++ b/convex/_generated/dataModel.d.ts @@ -8,29 +8,29 @@ * @module */ -import { AnyDataModel } from "convex/server"; +import type { + DataModelFromSchemaDefinition, + DocumentByName, + TableNamesInDataModel, + SystemTableNames, +} from "convex/server"; import type { GenericId } from "convex/values"; - -/** - * No `schema.ts` file found! - * - * This generated code has permissive types like `Doc = any` because - * Convex doesn't know your schema. If you'd like more type safety, see - * https://docs.convex.dev/using/schemas for instructions on how to add a - * schema file. - * - * After you change a schema, rerun codegen with `npx convex dev`. - */ +import schema from "../schema.js"; /** * The names of all of your Convex tables. */ -export type TableNames = string; +export type TableNames = TableNamesInDataModel; /** * The type of a document stored in Convex. + * + * @typeParam TableName - A string literal type of the table name (like "users"). */ -export type Doc = any; +export type Doc = DocumentByName< + DataModel, + TableName +>; /** * An identifier for a document in Convex. @@ -42,8 +42,10 @@ export type Doc = any; * * IDs are just strings at runtime, but this type can be used to distinguish them from other * strings when type checking. + * + * @typeParam TableName - A string literal type of the table name (like "users"). */ -export type Id = +export type Id = GenericId; /** @@ -55,4 +57,4 @@ export type Id = * This type is used to parameterize methods like `queryGeneric` and * `mutationGeneric` to make them type-safe. */ -export type DataModel = AnyDataModel; +export type DataModel = DataModelFromSchemaDefinition; diff --git a/convex/myFunctions.ts b/convex/myFunctions.ts new file mode 100644 index 0000000..0896dc3 --- /dev/null +++ b/convex/myFunctions.ts @@ -0,0 +1,78 @@ +import { v } from "convex/values"; +import { query, mutation, action } from "./_generated/server"; +import { api } from "./_generated/api"; + +// Write your Convex functions in any file inside this directory (`convex`). +// See https://docs.convex.dev/functions for more. + +// You can read data from the database via a query: +export const listNumbers = query({ + // Validators for arguments. + args: { + count: v.number(), + }, + + // Query implementation. + handler: async (ctx, args) => { + //// Read the database as many times as you need here. + //// See https://docs.convex.dev/database/reading-data. + const numbers = await ctx.db + .query("numbers") + // Ordered by _creationTime, return most recent + .order("desc") + .take(args.count); + return { + viewer: (await ctx.auth.getUserIdentity())?.name ?? null, + numbers: numbers.reverse().map((number) => number.value), + }; + }, +}); + +// You can write data to the database via a mutation: +export const addNumber = mutation({ + // Validators for arguments. + args: { + value: v.number(), + }, + + // Mutation implementation. + handler: async (ctx, args) => { + //// Insert or modify documents in the database here. + //// Mutations can also read from the database like queries. + //// See https://docs.convex.dev/database/writing-data. + + const id = await ctx.db.insert("numbers", { value: args.value }); + + console.log("Added new document with id:", id); + // Optionally, return a value from your mutation. + // return id; + }, +}); + +// You can fetch data from and send data to third-party APIs via an action: +export const myAction = action({ + // Validators for arguments. + args: { + first: v.number(), + second: v.string(), + }, + + // Action implementation. + handler: async (ctx, args) => { + //// Use the browser-like `fetch` API to send HTTP requests. + //// See https://docs.convex.dev/functions/actions#calling-third-party-apis-and-using-npm-packages. + // const response = await ctx.fetch("https://api.thirdpartyservice.com"); + // const data = await response.json(); + + //// Query data by running Convex queries. + const data = await ctx.runQuery(api.myFunctions.listNumbers, { + count: 10, + }); + console.log(data); + + //// Write data by running Convex mutations. + await ctx.runMutation(api.myFunctions.addNumber, { + value: args.first, + }); + }, +}); diff --git a/convex/schema.ts b/convex/schema.ts new file mode 100644 index 0000000..b1bc23e --- /dev/null +++ b/convex/schema.ts @@ -0,0 +1,12 @@ +import { defineSchema, defineTable } from "convex/server"; +import { v } from "convex/values"; + +// The schema is entirely optional. +// You can delete this file (schema.ts) and the +// app will continue to work. +// The schema provides more precise TypeScript types. +export default defineSchema({ + numbers: defineTable({ + value: v.number(), + }), +}); From 5f6c782f74f6acb8c34f6795decda48ff3cbf0cc Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Fri, 17 May 2024 14:54:32 +0200 Subject: [PATCH 10/24] Add convex client #client --- src/main.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.tsx b/src/main.tsx index eff7ccc..4880906 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,14 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import { ConvexProvider, ConvexReactClient } from "convex/react"; import "./index.css"; import App from "./App.tsx"; +const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string); createRoot(document.getElementById("root")!).render( - + + + , ); From 9187af783b7ed67f8ecb8cc37e075970c7b252c8 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 16:19:23 -0800 Subject: [PATCH 11/24] Set up tailwind --- src/index.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/index.css b/src/index.css index f1d8c73..62c2b40 100644 --- a/src/index.css +++ b/src/index.css @@ -1 +1,22 @@ @import "tailwindcss"; + +@theme { + --color-light: #ffffff; + --color-dark: #171717; +} + +@media (prefers-color-scheme: dark) { + body { + color: var(--color-light); + background: var(--color-dark); + font-family: Arial, Helvetica, sans-serif; + } +} + +@media (prefers-color-scheme: light) { + body { + color: var(--color-dark); + background: var(--color-light); + font-family: Arial, Helvetica, sans-serif; + } +} From 56f063a2f29d3004ca0602332de8252f71e26dd8 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 16:20:04 -0800 Subject: [PATCH 12/24] Add UI --- src/App.tsx | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index edbea37..588725b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,124 @@ +"use client"; + +import { useMutation, useQuery } from "convex/react"; +import { api } from "../convex/_generated/api"; + export default function App() { - return "Hello world!"; + return ( + <> +
+ Convex + React +
+
+

Convex + React

+ +
+ + ); +} + +function Content() { + const { viewer, numbers } = + useQuery(api.myFunctions.listNumbers, { + count: 10, + }) ?? {}; + const addNumber = useMutation(api.myFunctions.addNumber); + + if (viewer === undefined || numbers === undefined) { + return ( +
+

loading... (consider a loading skeleton)

+
+ ); + } + + return ( +
+

Welcome {viewer ?? "Anonymous"}!

+

+ Click the button below and open this page in another window - this data + is persisted in the Convex cloud database! +

+

+ +

+

+ Numbers:{" "} + {numbers?.length === 0 + ? "Click the button!" + : (numbers?.join(", ") ?? "...")} +

+

+ Edit{" "} + + convex/myFunctions.ts + {" "} + to change your backend +

+

+ Edit{" "} + + src/App.tsx + {" "} + to change your frontend +

+
+

Useful resources:

+
+
+ + +
+
+ + +
+
+
+
+ ); +} + +function ResourceCard({ + title, + description, + href, +}: { + title: string; + description: string; + href: string; +}) { + return ( +
+ + {title} + +

{description}

+
+ ); } From ab0fcea930bedc05ff8e429df7136c5e63d82a8b Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Tue, 16 Jul 2024 15:37:13 +0200 Subject: [PATCH 13/24] Add README #template-react-vite --- README.md | 88 +++++++++++++++++++++++-------------------------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index bee7491..0b8c20c 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,40 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default tseslint.config({ - extends: [ - // Remove ...tseslint.configs.recommended and replace with this - ...tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - ...tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - ...tseslint.configs.stylisticTypeChecked, - ], - languageOptions: { - // other options... - parserOptions: { - project: ["./tsconfig.node.json", "./tsconfig.app.json"], - tsconfigRootDir: import.meta.dirname, - }, - }, -}); +# Welcome to your Convex + React (Vite) app + +This is a [Convex](https://convex.dev/) project created with [`npm create convex`](https://www.npmjs.com/package/create-convex). + +After the initial setup (<2 minutes) you'll have a working full-stack app using: + +- Convex as your backend (database, server logic) +- [React](https://react.dev/) as your frontend (web page interactivity) +- [Vite](https://vitest.dev/) for optimized web hosting +- [Tailwind](https://tailwindcss.com/) for building great looking accessible UI + +## Get started + +If you just cloned this codebase and didn't use `npm create convex`, run: + ``` +npm install +npm run dev +``` + +If you're reading this README on GitHub and want to use this template, run: -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from "eslint-plugin-react-x"; -import reactDom from "eslint-plugin-react-dom"; - -export default tseslint.config({ - plugins: { - // Add the react-x and react-dom plugins - "react-x": reactX, - "react-dom": reactDom, - }, - rules: { - // other rules... - // Enable its recommended typescript rules - ...reactX.configs["recommended-typescript"].rules, - ...reactDom.configs.recommended.rules, - }, -}); ``` +npm create convex@latest -- -t react-vite +``` + +## Learn more + +To learn more about developing your project with Convex, check out: + +- The [Tour of Convex](https://docs.convex.dev/get-started) for a thorough introduction to Convex principles. +- The rest of [Convex docs](https://docs.convex.dev/) to learn about all Convex features. +- [Stack](https://stack.convex.dev/) for in-depth articles on advanced topics. + +## Join the community + +Join thousands of developers building full-stack apps with Convex: + +- Join the [Convex Discord community](https://convex.dev/community) to get help in real-time. +- Follow [Convex on GitHub](https://github.com/get-convex/), star and contribute to the open-source implementation of Convex. From 9c4267564c1732ec230332d776bd6f83ca29aac8 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:03:52 -0800 Subject: [PATCH 14/24] Install Convex Auth npm i @convex-dev/auth --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 93bc7c0..5c8df57 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "preview": "vite preview" }, "dependencies": { + "@convex-dev/auth": "^0.0.81", "convex": "^1.23.0", "react": "^19.0.0", "react-dom": "^19.0.0" From 02519449fbea7fb0576285af6d9fe48814198aae Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:05:59 -0800 Subject: [PATCH 15/24] Run Convex Auth setup command // This should be: // npx @convex-dev/auth // But we need a way to skip the env vars setup --- convex/auth.config.ts | 8 ++++++++ convex/auth.ts | 5 +++++ convex/http.ts | 8 ++++++++ 3 files changed, 21 insertions(+) create mode 100644 convex/auth.config.ts create mode 100644 convex/auth.ts create mode 100644 convex/http.ts diff --git a/convex/auth.config.ts b/convex/auth.config.ts new file mode 100644 index 0000000..f4eb564 --- /dev/null +++ b/convex/auth.config.ts @@ -0,0 +1,8 @@ +export default { + providers: [ + { + domain: process.env.CONVEX_SITE_URL, + applicationID: "convex", + }, + ], +}; diff --git a/convex/auth.ts b/convex/auth.ts new file mode 100644 index 0000000..cdfe776 --- /dev/null +++ b/convex/auth.ts @@ -0,0 +1,5 @@ +import { convexAuth } from "@convex-dev/auth/server"; + +export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({ + providers: [], +}); diff --git a/convex/http.ts b/convex/http.ts new file mode 100644 index 0000000..1816e5b --- /dev/null +++ b/convex/http.ts @@ -0,0 +1,8 @@ +import { httpRouter } from "convex/server"; +import { auth } from "./auth"; + +const http = httpRouter(); + +auth.addHttpRoutes(http); + +export default http; From 6c1a4556fb18a9dc8c444e3f35a7d4c6b21b1239 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Mon, 10 Feb 2025 13:14:27 -0800 Subject: [PATCH 16/24] Add setup script --- package.json | 3 ++- setup.mjs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 setup.mjs diff --git a/package.json b/package.json index 5c8df57..ad7c0fa 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "npm-run-all --parallel dev:frontend dev:backend", "dev:frontend": "vite --open", "dev:backend": "convex dev", - "predev": "convex dev --until-success && convex dashboard", + "predev": "convex dev --until-success && convex dev --once --run-sh 'node setup.mjs --once' && convex dashboard", "build": "tsc -b && vite build", "lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" @@ -25,6 +25,7 @@ "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", + "dotenv": "^16.4.7", "eslint": "^9.21.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", diff --git a/setup.mjs b/setup.mjs new file mode 100644 index 0000000..0ffb1ae --- /dev/null +++ b/setup.mjs @@ -0,0 +1,35 @@ +/** + * This script runs `npx @convex-dev/auth` to help with setting up + * environment variables for Convex Auth. + * + * You can safely delete it and remove it from package.json scripts. + */ + +import fs from "fs"; +import { config as loadEnvFile } from "dotenv"; +import { spawnSync } from "child_process"; + +if (!fs.existsSync(".env.local")) { + // Something is off, skip the script. + process.exit(0); +} + +const config = {}; +loadEnvFile({ path: ".env.local", processEnv: config }); + +const runOnceWorkflow = process.argv.includes("--once"); + +if (runOnceWorkflow && config.SETUP_SCRIPT_RAN !== undefined) { + // The script has already ran once, skip. + process.exit(0); +} + +const result = spawnSync("npx", ["@convex-dev/auth", "--skip-git-check"], { + stdio: "inherit", +}); + +if (runOnceWorkflow) { + fs.writeFileSync(".env.local", `\nSETUP_SCRIPT_RAN=1\n`, { flag: "a" }); +} + +process.exit(result.status); From 669e2c5e967050201164e15882aada20c36bd40c Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:06:30 -0800 Subject: [PATCH 17/24] Run convex codegen npx convex codegen --- convex/_generated/api.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 6a067d5..09899f7 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -13,6 +13,8 @@ import type { FilterApi, FunctionReference, } from "convex/server"; +import type * as auth from "../auth.js"; +import type * as http from "../http.js"; import type * as myFunctions from "../myFunctions.js"; /** @@ -24,6 +26,8 @@ import type * as myFunctions from "../myFunctions.js"; * ``` */ declare const fullApi: ApiFromModules<{ + auth: typeof auth; + http: typeof http; myFunctions: typeof myFunctions; }>; export declare const api: FilterApi< From 2c12d808d3ca826b6060aa0a0e6a3a5e85a166e9 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:07:48 -0800 Subject: [PATCH 18/24] Enable Password provider --- convex/auth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/convex/auth.ts b/convex/auth.ts index cdfe776..e292e03 100644 --- a/convex/auth.ts +++ b/convex/auth.ts @@ -1,5 +1,6 @@ import { convexAuth } from "@convex-dev/auth/server"; +import { Password } from "@convex-dev/auth/providers/Password"; export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({ - providers: [], + providers: [Password], }); From 618abe03946ed2d5df17ba70048854d5309f2845 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Mon, 1 Jul 2024 15:16:06 +0200 Subject: [PATCH 19/24] Add auth tables to schema --- convex/schema.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/convex/schema.ts b/convex/schema.ts index b1bc23e..cae4774 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -1,11 +1,12 @@ import { defineSchema, defineTable } from "convex/server"; import { v } from "convex/values"; +import { authTables } from "@convex-dev/auth/server"; -// The schema is entirely optional. -// You can delete this file (schema.ts) and the -// app will continue to work. +// The schema is normally optional, but Convex Auth +// requires indexes defined on `authTables`. // The schema provides more precise TypeScript types. export default defineSchema({ + ...authTables, numbers: defineTable({ value: v.number(), }), From 7724c091c2ac61324bf9b72f525ab61b0a55ed95 Mon Sep 17 00:00:00 2001 From: Michal Srb Date: Mon, 1 Jul 2024 15:17:38 +0200 Subject: [PATCH 20/24] Add ConvexAuthProvider --- src/main.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 4880906..5f5a1bd 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,14 +1,16 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; -import { ConvexProvider, ConvexReactClient } from "convex/react"; +import { ConvexAuthProvider } from "@convex-dev/auth/react"; +import { ConvexReactClient } from "convex/react"; import "./index.css"; import App from "./App.tsx"; const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string); + createRoot(document.getElementById("root")!).render( - + - + , ); From ddf9cf32425569b5cf04016a916c57afc8be28b5 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:53:38 -0800 Subject: [PATCH 21/24] Update myFunctions for Convex Auth --- convex/myFunctions.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/convex/myFunctions.ts b/convex/myFunctions.ts index 0896dc3..a1b7a04 100644 --- a/convex/myFunctions.ts +++ b/convex/myFunctions.ts @@ -1,6 +1,7 @@ import { v } from "convex/values"; import { query, mutation, action } from "./_generated/server"; import { api } from "./_generated/api"; +import { getAuthUserId } from "@convex-dev/auth/server"; // Write your Convex functions in any file inside this directory (`convex`). // See https://docs.convex.dev/functions for more. @@ -21,8 +22,10 @@ export const listNumbers = query({ // Ordered by _creationTime, return most recent .order("desc") .take(args.count); + const userId = await getAuthUserId(ctx); + const user = userId === null ? null : await ctx.db.get(userId); return { - viewer: (await ctx.auth.getUserIdentity())?.name ?? null, + viewer: user?.email ?? null, numbers: numbers.reverse().map((number) => number.value), }; }, From 70052a4c33de41794e9b78f3d72ea9e1a7a67556 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 17:55:18 -0800 Subject: [PATCH 22/24] Add UI for Convex Auth --- src/App.tsx | 102 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 588725b..2bdbec1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,22 +1,116 @@ "use client"; -import { useMutation, useQuery } from "convex/react"; +import { + Authenticated, + Unauthenticated, + useConvexAuth, + useMutation, + useQuery, +} from "convex/react"; import { api } from "../convex/_generated/api"; +import { useAuthActions } from "@convex-dev/auth/react"; +import { useState } from "react"; export default function App() { return ( <>
- Convex + React + Convex + React + Convex Auth +
-

Convex + React

- +

+ Convex + React + Convex Auth +

+ + + + + +
); } +function SignOutButton() { + const { isAuthenticated } = useConvexAuth(); + const { signOut } = useAuthActions(); + return ( + <> + {isAuthenticated && ( + + )} + + ); +} + +function SignInForm() { + const { signIn } = useAuthActions(); + const [flow, setFlow] = useState<"signIn" | "signUp">("signIn"); + const [error, setError] = useState(null); + return ( +
+

Log in to see the numbers

+
{ + e.preventDefault(); + const formData = new FormData(e.target as HTMLFormElement); + formData.set("flow", flow); + void signIn("password", formData).catch((error) => { + setError(error.message); + }); + }} + > + + + +
+ + {flow === "signIn" + ? "Don't have an account?" + : "Already have an account?"} + + setFlow(flow === "signIn" ? "signUp" : "signIn")} + > + {flow === "signIn" ? "Sign up instead" : "Sign in instead"} + +
+ {error && ( +
+

+ Error signing in: {error} +

+
+ )} +
+
+ ); +} + function Content() { const { viewer, numbers } = useQuery(api.myFunctions.listNumbers, { From fd4952f283725d03307d375bb76b0d7ec9eb106c Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Wed, 5 Feb 2025 19:28:53 -0800 Subject: [PATCH 23/24] Update README --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b8c20c..af0a7c4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Welcome to your Convex + React (Vite) app +# Welcome to your Convex + React (Vite) + Convex Auth app This is a [Convex](https://convex.dev/) project created with [`npm create convex`](https://www.npmjs.com/package/create-convex). @@ -7,7 +7,8 @@ After the initial setup (<2 minutes) you'll have a working full-stack app using: - Convex as your backend (database, server logic) - [React](https://react.dev/) as your frontend (web page interactivity) - [Vite](https://vitest.dev/) for optimized web hosting -- [Tailwind](https://tailwindcss.com/) for building great looking accessible UI +- [Tailwind](https://tailwindcss.com/) for building great looking UI +- [Convex Auth](https://labs.convex.dev/auth) for authentication ## Get started @@ -21,9 +22,13 @@ npm run dev If you're reading this README on GitHub and want to use this template, run: ``` -npm create convex@latest -- -t react-vite +npm create convex@latest -- -t react-vite-convexauth ``` +For more information on how to configure Convex Auth, check out the [Convex Auth docs](https://labs.convex.dev/auth/). + +For more examples of different Convex Auth flows, check out this [example repo](https://www.convex.dev/templates/convex-auth). + ## Learn more To learn more about developing your project with Convex, check out: From e051d0df63b1e5b01c6aee977fcc5abbac0a6abf Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Thu, 10 Apr 2025 15:41:22 -0400 Subject: [PATCH 24/24] fix predev script quotes for windows --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad7c0fa..0c838a5 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "dev": "npm-run-all --parallel dev:frontend dev:backend", "dev:frontend": "vite --open", "dev:backend": "convex dev", - "predev": "convex dev --until-success && convex dev --once --run-sh 'node setup.mjs --once' && convex dashboard", + "predev": "convex dev --until-success && convex dev --once --run-sh \"node setup.mjs --once\" && convex dashboard", "build": "tsc -b && vite build", "lint": "tsc && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview"