Skip to main content

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 FileImplementation Summary
src/types/todo.d.tsDefine Todo types
src/states/IsLoading.tsDefine data processing state
src/hooks/useTodoSaver.tsxDefine custom hook for saving Todos
src/components/TodoListItem.tsxImplement TodoList items as reusable components
src/pages/todolist/index.pages.tsxDefine TodoList page UI
src/routes/todolist.tsxTodoList page entry point and server-side processing (loader, action, screen entry point)
src/routes.tsDefine 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.