Skip to main content

Intro


This tutorial walks through building a shared Todo app with React, TypeScript, Redux Toolkit, and AppDB. The app is scaffolded with the DA CLI, loads the current Domo user’s identity and avatar, and stamps ownership onto every todo so users can see who added what. You’ll learn the patterns you’ll reach for in every real Domo app:
  • Scaffold a Vite + React + TypeScript project with da new
  • Declare an AppDB collection in manifest.json and publish an initial design
  • Build a typed service layer over AppDBClient, IdentityClient, and UserClient
  • Manage async state with Redux Toolkit’s buildCreateSlice + asyncThunkCreator
  • Compose small, presentational components that read from and dispatch against the store
The finished code is at DomoApps/basic-react-app-todo-tutorial on GitHub.
Prerequisite: Complete the Setup and Installation guide and run domo login before starting.

Step 1: Install the DA CLI and scaffold the app


The DA CLI clones the @domoinc/vite-react-template — a Vite + React + TypeScript project preconfigured with the Domo proxy, ESLint, Prettier, Vitest, Storybook, and da generate scaffolding. Install the CLI globally:
Create the project:
da new prompts for a package manager (pick pnpm), clones the template, writes your app name, initializes git, and installs dependencies.
App names must be lowercase with hyphens only. Capitals, underscores, and periods are rejected.
The scaffold gives you:
  • public/manifest.json — app metadata, size, collections
  • public/thumbnail.png — thumbnail shown in the Asset Library
  • src/main.tsx — React entry point
  • src/manifestOverrides.json — per-environment manifest overrides (dev/qa/prod)
  • A Vite dev server wired through @domoinc/ryuu-proxy to your Domo instance
  • Scripts: start, build, upload, generate, test, storybook
Add the two runtime dependencies we’ll use beyond the scaffold:

Step 2: Declare the Todos collection


Replace public/manifest.json with:
Note: AppDB columns are always STRING. Booleans are stored as "true" / "false" and parsed at the UI layer. ownerId and ownerName give us the basis for per-user ownership displays and filters.

Step 3: Publish the initial design and wire the proxy


Before local dev can read or write documents, Domo needs to provision the collection and hand you a proxyId.
This runs pnpm build and domo publish from the build/ folder. The output prints a link to the new App Design. Then, in the Domo UI:
  1. Open the App Design link.
  2. Click New CardSelect CollectionCreate NewCreate Collection to provision an empty Todos collection.
  3. Save the card.
Copy the App Design id and the card’s proxyId from the design page, and add both to public/manifest.json:
For multi-environment apps, use da manifest to add overrides to src/manifestOverrides.json instead of hand-editing manifest.json. da apply-manifest runs automatically as a prestart / prebuild hook.

Step 4: Types and service layer


Create src/services/types.ts:
Create src/services/app.ts:
Two things worth calling out:
  • IdentityClient + UserClient give you the current user’s ID, email, display name, role, and avatar — everything you need to stamp ownership on documents and render a friendly header.
  • extractDocs flattens the AppDB envelope ({ id, content, createdOn, ... }) into plain todo objects, so the rest of the app never has to think about document metadata.

Step 5: Redux store with buildCreateSlice


We’ll use Redux Toolkit’s buildCreateSlice with the asyncThunkCreator so that async thunks can be declared inline on each slice — no separate actions.ts file. Create src/reducers/createAppSlice.ts:
Create src/reducers/index.ts:
App slice — user identity. Create src/reducers/app/slice.ts:
Todos slice — CRUD thunks. Create src/reducers/todos/slice.ts:
A few patterns to notice:
  • loading vs savingloading covers the initial list fetch; saving is toggled by creates, updates, and toggles so the form can disable its submit button without blanking the list.
  • toggleTodo takes the whole todo (not just an ID) so the reducer can build the full TodoData payload without refetching.
  • Selectors declared on the slice — callers never reach into state.todos.items directly; they import selectTodos and get type safety for free.

Step 6: Components


Each component lives in its own folder under src/components/ with a matching .module.scss. We’ll show the TypeScript; grab the SCSS from the GitHub source. UserHeader renders the current user’s avatar, name, role, and email, with a gradient-initials fallback if the avatar URL 404s.
TodoForm is a simple controlled form that stamps ownerId / ownerName from the store onto every new todo at submit time.
TodoItem renders a single row — checkbox, title, owner, due date, and delete button. A priority class on the container drives the colored left border.
TodoList handles filtering (all / active / completed) and the count header.
App is the root: it kicks off both initial loads in a useEffect, surfaces any error from either slice in a banner, and renders the header, form, and list.

Step 7: Wire up main.tsx


Replace src/main.tsx with:
DOMO_APP_NAME and DOMO_APP_VERSION are injected by the Vite template from manifest.json — useful for logging and telemetry. Your final src/ tree:

Step 8: Test locally


The Vite dev server starts on port 3000 (or 3001/3002 if busy). Because proxyId is set, AppDBClient, IdentityClient, and UserClient all talk to your real Domo instance — create, toggle, and delete todos and they persist across reloads. Log in as a different user and you’ll see your name/avatar in the header and By <name> on every new todo.

Step 9: Publish


The script runs pnpm build (which lints, tests, and builds), then domo publish from the build/ folder. The new build becomes the active design. Anyone instantiating the app from the Asset Library picks it up.

Next steps


  • Run da generate component or da generate reducer to scaffold new slices that follow the same createAppSlice pattern.
  • Add a UserPreferences collection with a limitToOwner filter to persist per-user theme or default filter — see AppDB filters.
  • Layer dev/qa/prod id + proxyId values with da manifest and src/manifestOverrides.json instead of hand-editing.
  • Continue with AI Book Recommender or Mapbox World Map.