Project: Build a Notes App
Combine everything — input, state, FlatList and AsyncStorage — into a real notes app that remembers your notes.
What you will learn
- Add and list notes with TextInput and FlatList
- Delete a note with a tap
- Save notes on the device so they survive a restart
What you will build
A working Notes app: type a note, tap Add, and it appears in a scrollable list. Tap a note to delete it. Best of all, the notes are saved on the phone, so they are still there after you close and reopen the app. You will use every core idea from this subject.
- TextInput + useState to type a new note.
- FlatList to show all notes.
- TouchableOpacity to delete a note on tap.
- AsyncStorage to save and load notes.
Step 1 — Add and list notes
Start simple: keep notes in state, add on a button tap, and show them with a FlatList.
import { useState } from 'react';
import { View, TextInput, Button, FlatList, Text, StyleSheet } from 'react-native';
export default function App() {
const [text, setText] = useState('');
const [notes, setNotes] = useState([]);
function addNote() {
if (text.trim() === '') return; // ignore empty notes
const note = { id: Date.now().toString(), text: text };
setNotes([note, ...notes]); // newest first
setText(''); // clear the box
}
return (
<View style={styles.screen}>
<TextInput
style={styles.input}
placeholder="Write a note..."
value={text}
onChangeText={setText}
/>
<Button title="Add" onPress={addNote} />
<FlatList
data={notes}
keyExtractor={(n) => n.id}
renderItem={({ item }) => <Text style={styles.note}>{item.text}</Text>}
/>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 20, paddingTop: 60 },
input: { borderWidth: 1, borderColor: '#ccc', padding: 10, borderRadius: 6, marginBottom: 10 },
note: { padding: 14, borderBottomWidth: 1, borderColor: '#eee', fontSize: 16 },
});Note: Output:
Type "Buy milk", tap Add -> it appears in the list and the box clears.
Type "Call mom", tap Add -> the list now shows:
Call mom
Buy milk
Using Date.now() as the id gives each note a unique key. Empty notes are ignored.
Step 2 — Delete a note on tap
Add a deleteNote function, then wrap each note in a TouchableOpacity so a tap removes it. The trick is filter: it builds a new list keeping every note except the one you tapped.
import { TouchableOpacity } from 'react-native';
// add this function inside App, next to addNote:
function deleteNote(id) {
setNotes(notes.filter((n) => n.id !== id)); // keep all but this id
}
// and change renderItem to make each row tappable:
<FlatList
data={notes}
keyExtractor={(n) => n.id}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => deleteNote(item.id)}>
<Text style={styles.note}>{item.text}</Text>
</TouchableOpacity>
)}
/>Note: Output:
Tap "Buy milk" and it disappears from the list, leaving:
Call mom
filter keeps every note whose id is not the one tapped, so only that note is removed. Because notes changed via setNotes, the FlatList redraws by itself.
Step 3 — Save notes on the device
Now make notes survive a restart. Load them once when the app opens, and save whenever they change, using AsyncStorage with JSON.
import { useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
// load saved notes once, when the app opens
useEffect(() => {
AsyncStorage.getItem('notes').then((raw) => {
if (raw) setNotes(JSON.parse(raw));
});
}, []);
// save whenever notes change
useEffect(() => {
AsyncStorage.setItem('notes', JSON.stringify(notes));
}, [notes]);Note: Output:
Add a few notes, fully close the app, and reopen it -> your notes are still there.
The first effect runs once to load. The second runs whenever notes changes (note the [notes]) and saves the latest list.
Tip: Order matters here. The load effect has [] so it runs only once, on open. The save effect has [notes] so it runs every time the list changes — including right after loading, which simply re-saves the same notes. That is harmless, so you do not need to worry about the two effects clashing.
Your tasks
- Get Step 1 working: add notes and see them in the list.
- Add Step 2 so tapping a note deletes it.
- Add Step 3 so notes are saved and reload after a restart.
- Polish it: style the input and notes, and add a heading like "My Notes".
Stretch goals
- Show a "No notes yet" message with
ListEmptyComponentwhen the list is empty. - Add a confirm step before deleting, using
Alert.alertwith two buttons. - Add a second screen (a stack) that shows one note in full when tapped.
Watch out: Build in small steps and test after each one. Get Step 1 fully working before adding delete, and add saving last. A small finished app beats a big broken one.
Note: When this works you have built a real, useful mobile app that remembers data on the device — using input, state, lists and storage together. Run it on your own phone and add it to your portfolio!
Q. In this app, which piece makes the notes survive after you fully close and reopen it?
useState is forgotten when the app closes. Saving to and loading from AsyncStorage is what keeps the notes between launches.✍️ Practice
- Add a "Clear all" button that empties the notes and the saved storage.
- Show the total number of notes in the heading, e.g. "My Notes (3)".
🏠 Homework
- Extend the app or rebuild it as a simple weather or expense tracker, reusing the same input + FlatList + AsyncStorage pattern. Write a short note on what you changed.