App development tutorial: build a daily checklist with Expo and React Native
By Gistmax Tutorials · 2026-11-04
Create a mobile checklist with three activities and a completion counter. The lesson demonstrates component state, immutable updates and accessible touch controls. It is a session-only prototype: closing or reloading the app resets the checklist.
1. Start a TypeScript project
Install a supported Node.js release, then run `npx create-expo-app@latest gistmax-checklist --template blank-typescript`. Enter the new folder and run `npx expo start`. Use an Expo-supported device or simulator compatible with the SDK selected by the template.
This tutorial uses the blank TypeScript template, whose entry screen is `App.tsx`. If you choose a router-based template instead, its route files are different. Replace the contents of `App.tsx` with the complete example below.
2. Build the screen
import React, { useState } from 'react';
import { SafeAreaView, Text, Pressable, View } from 'react-native';
const initial = [
{ id: 'read', title: 'Read one tutorial', done: false },
{ id: 'build', title: 'Build one small example', done: false },
{ id: 'reflect', title: 'Write down one lesson', done: false },
];
export default function App() {
const [items, setItems] = useState(initial);
const completed = items.filter(item => item.done).length;
function toggle(id: string) {
setItems(current => current.map(item =>
item.id === id ? { ...item, done: !item.done } : item
));
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#f3f6f4' }}>
<View style={{ padding: 24, gap: 16 }}>
<Text style={{ fontSize: 30, fontWeight: '700' }}>Today's learning</Text>
<Text accessibilityLiveRegion="polite">{completed} of {items.length} complete</Text>
{items.map(item => (
<Pressable key={item.id} accessibilityRole="checkbox"
accessibilityLabel={item.title} accessibilityState={{ checked: item.done }}
onPress={() => toggle(item.id)}
style={{ padding: 20, borderRadius: 12, backgroundColor: item.done ? '#d3eadb' : '#ffffff' }}>
<Text style={{ fontSize: 18 }}>{item.done ? '✓ ' : '○ '}{item.title}</Text>
</Pressable>
))}
<Pressable accessibilityRole="button" onPress={() => setItems(initial)} style={{ padding: 16 }}>
<Text>Reset checklist</Text>
</Pressable>
</View>
</SafeAreaView>
);
}3. Verify the behaviour
Tap one activity: the count should become one. Tap it again: the count should return to zero. Complete all three and press Reset. The visual marks and completion counter should agree after every action.
The updater receives the current state and returns a new array. It creates a new object only for the changed item. Avoid modifying the existing item's `done` property directly; predictable state updates make the screen easier to reason about as it grows.
4. Check accessibility and limitations
Use your device's screen reader to explore the controls. Each activity is labelled as a checkbox with a checked state, so completion is not communicated through colour alone. Test the touch targets on a small screen and with larger text settings.
This example does not define what happens at midnight or preserve previous days. Before adding persistence, decide whether you need a daily history, a timezone-aware reset or a simple reusable checklist. Those are product decisions, not just storage choices.
Troubleshooting and extension
If the device cannot connect to the development server, follow Expo's connection guidance for your environment. If you cannot find `App.tsx`, verify the template you selected. For the next lesson, add an activity input with a stable ID and reject empty titles. Keep persistence as a separate step so you can test it independently.
References
Expo first-app guide · React Native Pressable · React state updates