Web Application Implementation Guide
This guide introduces recommended application implementation patterns using React Router v7 (Framework mode).
Implementing TODO List Display and State Update Functionality
We will implement the functionality to display a TODO List and update its state. While the implementation includes save button functionality after updating todos, this catalog doesn't perform actual database operations but simulates the process by showing loaders up to the point where such operations would occur.
-
Welcome page
/
-
Todo list page
/todolist
Prerequisites
Implementation methods for basic components like root.tsx and layout are partially omitted. However, the content of this catalog is almost identical to what's generated when creating a new React Router v7 project.
Overall Process Flow
As a recommended configuration, we will implement in the following order, assuming after the initial setup of the entire project.
Target File | Implementation Summary |
---|---|
src/types/todo.d.ts | Define Todo types |
src/states/IsLoading.ts | Define data processing state |
src/hooks/useTodoSaver.tsx | Define custom hook for saving Todos |
src/components/TodoListItem.tsx | Implement TodoList items as reusable components |
src/pages/todolist/index.pages.tsx | Define TodoList page UI |
src/routes/todolist.tsx | TodoList page entry point and server-side processing (loader, action, screen entry point) |
src/routes.ts | Define TodoList page routing |
Additionally, please configure directories and files as needed.
src/types/todo.d.ts
Define Todo Types
Define types as follows. These will be used for Props definitions in each component and type declarations for source data.
export type Todo = {
id: string
title: string
status: 'pending' | 'inProgress' | 'completed'
isStarred: boolean
// ...
}
Also, defining the following in src/types/index.ts
simplifies import statements, so it's recommended to create this in all directories under src/
.
export * from './Todo.d'
src/states/IsLoading.ts
Define Data Processing State
Declare IsLoading state as follows. This catalog uses the Jotai library.
import { atom } from 'jotai'
export const IsLoadingAtom = atom<boolean>(false)
src/hooks/useTodoSaver.tsx
Define Custom Hook for Saving Todos
Implement the process for saving Todo changes. While this catalog doesn't perform actual update processing, it implements sending updated Todos to PUT /todolist
and logging the output.
import { useAtom } from 'jotai'
import * as Atoms from '~/states'
export const useTodoSaver = () => {
const [isLoading, setIsLoading] = useAtom(Atoms.IsLoadingAtom)
const saveTodo = async (todo: Todo) => {
setIsLoading(true)
const saveResult = fetch('/todolist', {
method: 'PUT',
headers: { 'content-type': 'application/json', body: JSON.stringify(todo) },
})
.then((res) => res.json())
.finally(() => setIsLoading(false))
console.log('## saveResult - ', saveResult)
}
return { isLoading, saveTodo }
}
src/components/TodoListItem.tsx
Implement TodoList Items as Reusable Components
Implement Todo list items. Such list item components are positioned and created with reusability for other screens in mind.
import type { Todo } from '~/types'
type Props = {
todo: Todo
}
export const TodoListItem: React.FC<Props> = ({ todo }) => {
// Some dynamic ui handler methods
return (
<div className="bg-white rounded-lg border border-gray-200 p-6 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-3">
<h3 className="text-lg font-medium text-gray-900 truncate">{todo.title}</h3>
{ /* Omitted for brevity. All sample code is included in the catalog AMI. */ }
</div>
</div>
</div>
</div>
)
}
src/pages/todolist/index.pages.tsx
Define TodoList Page UI
Implement the UI for the TodoList page that displays the Todo list.
import { TodoListItem } from '~/components'
import type { Todo } from '~/types'
type Props = {
items: Todo[]
}
export const TodoListPage: React.FC<Props> = (props) => {
const { items } = props
return (
<>
<main className="max-w-6xl mx-auto px-4 py-8">
{ /* Omitted for brevity. All sample code is included in the catalog AMI. */ }
<div className="space-y-4">
{items.map((todo) => (
<TodoListItem key={todo.id} todo={todo} />
))}
</div>
</main>
</>
)
}
src/routes/todolist.tsx
Implement TodoList Entry Point and Server-side Processing
Under src/routes/
, you can configure server-side processing (loader, action) and various settings like meta tags when loading the corresponding screen. It's possible to fetch data from the server side and pass it to the Page component from the previous section via Props or other means. You can also implement loaders on separate routes and take a SPA-like approach asynchronously with GET /api/todolist
.
import type { MetaFunction } from 'react-router'
import type { Route } from './+types/todolist'
import type { Todo } from '~/types'
import { TODO_ITEMS } from '~/fixtures'
import { TodoListPage } from '~/pages'
export const meta: MetaFunction = () => {
// biome-ignore format: for better readability
return [
{ title: 'New React Router App - Todo List Page' },
{ name: 'description', content: 'Welcome to React Router!' }
]
}
export async function loader({/* params (path parameters) */}: Route.LoaderArgs) {
return { TODO_ITEMS }
}
const TodoListRoute = ({ loaderData }: { loaderData: { TODO_ITEMS: Todo[] } }) => {
const { TODO_ITEMS } = loaderData
return <TodoListPage items={TODO_ITEMS} />
}
export default TodoListRoute
For details, please refer to the React Router v7 official documentation.
src/routes.ts
Define TodoList Page Routing
Finally, configure which routes file to route to when opening the screen. Here, set it up to be routed to src/routes/todolist.tsx
.
import { layout, type RouteConfig, route } from '@react-router/dev/routes'
const PREFIX = './routes'
export default [
// biome-ignore format: for better readability
layout('./layouts/default.tsx', [
// Welcome page
route('/', `${PREFIX}/welcome.tsx`),
// Todo list page
route('/todolist', `${PREFIX}/todolist.tsx`),
]),
] satisfies RouteConfig
Testing
Once you've progressed this far, launch the application in development mode and perform functionality testing. Proceed to local execution debugging and testing.