Adding a feature
This walkthrough adds a Todos feature: an API hook, a screen with a list, a route, translations, and a persisted filter. The same steps work for any feature.
-
Create the folder
src/features/todos/├── api/│ ├── types.ts│ └── use-todos.ts├── screens/│ └── TodosScreen.tsx└── index.ts -
Add the API hook
src/features/todos/api/types.ts export type Todo = { id: number; todo: string; completed: boolean };export type TodosPage = { todos: Todo[]; total: number; skip: number; limit: number };src/features/todos/api/use-todos.ts import { createQuery } from 'react-query-kit';import type { AxiosError } from 'axios';import { client } from '@/services';import type { TodosPage } from './types';export const useTodos = createQuery<TodosPage, void, AxiosError>({queryKey: ['todos'],fetcher: (_, { signal }) => client.get('todos', { signal }).then((r) => r.data),});DummyJSON has a
todosendpoint, so this works against the development API as is. -
Add translations
Add a
todossection toen.json,es.jsonandar.json:src/localization/translations/en.json "todos": {"title": "Todos","empty": "Nothing to do"} -
Build the screen
src/features/todos/screens/TodosScreen.tsx import { FlatList, Text } from 'react-native';import { useTranslate } from '@/localization/utils';import ScreenWrapper from '@/shared/components/ScreenWrapper';import { useScreenLoader } from '@/shared/hooks';import { TextStyles } from '@/theme';import { useTodos } from '../api/use-todos';export function TodosScreen() {const t = useTranslate();const { data, isLoading } = useTodos();useScreenLoader(isLoading);return (<ScreenWrapper><Text style={TextStyles.h1}>{t('todos.title')}</Text><FlatListdata={data?.todos ?? []}keyExtractor={(item) => String(item.id)}renderItem={({ item }) => <Text>{item.todo}</Text>}ListEmptyComponent={<Text>{t('todos.empty')}</Text>}/></ScreenWrapper>);}Inside the feature, imports are relative (
../api/use-todos). -
Export the public API
src/features/todos/index.ts export { TodosScreen } from './screens/TodosScreen';export { useTodos } from './api/use-todos';export type { Todo } from './api/types'; -
Add a route
src/app/todos.tsx export { TodosScreen as default } from '@/features/todos';That’s the
/todosroute. Navigate withrouter.push('/todos')or<Link href="/todos">.To make it a tab instead, put the file in
src/app/(tabs)/, add<Tabs.Screen name="todos" />tosrc/app/(tabs)/_layout.tsx, and register its icon and label inTabBarIcon.tsxandTabBarLabel.tsx.Add the route name to
NAVIGATIONinsrc/shared/constants/navigation.ts, add it to the param list insrc/navigation/types.ts, then register the screen:src/navigation/MainTabNavigator.tsx <Tab.Screen component={TodosScreen} name={NAVIGATION.todos} />Add a path for it in
src/navigation/linking.tsif it should open from a deep link. -
Add state (if you need it)
For app state that should survive restarts, like a “hide completed” toggle, add a store inside the feature and expose a hook from
index.ts. See State management. Remember to register Zustand stores inrehydrateStores(). -
Test it
Terminal window yarn lint && yarn type-check && yarn test
Checklist
Section titled “Checklist”- Everything lives in
src/features/<name>/ - Other code imports it only through
index.ts - API calls use
clientfrom@/servicesand passsignal - Every string is in all three translation files
- New Zustand stores are added to
rehydrateStores()