Skip to content

kuraydev/react-native-typescript-boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

React Native Typescript Boilerplate npm version npm Platform - Android and iOS License: MIT styled with prettier AI Ready

AI-Ready. Production-grade React Native + TypeScript boilerplate with a built-in, provider-agnostic AI service layer. Wire up OpenAI, Anthropic Claude, Google Gemini β€” or any LLM β€” in minutes, not days.

Tip

Works great with AI coding assistants. This repo ships with purpose-built guidance files so Cursor, Claude Code, GitHub Copilot, Windsurf, and Gemini CLI all understand the project conventions out of the box:

File Used by
CLAUDE.md Claude Code
AGENTS.md Windsurf Β· Codex CLI Β· Gemini CLI
.cursor/rules/ Cursor
.github/copilot-instructions.md GitHub Copilot

Note

All screens, components, and mock data included in this boilerplate are for demonstration purposes only. They exist to showcase the architecture, theming, navigation, and AI service layer β€” not to be kept as-is. Feel free to delete any screen, component, or piece of mock data and replace it with your own.


Table of Contents


πŸ“Έ Showcase

Home Screen Β Β  Search Screen Β Β  Notifications Screen Β Β  Settings Screen


πŸ€– What's New in v6

  • AI-Ready β€” provider-agnostic AI service layer for OpenAI, Anthropic, and Gemini
  • useAIChat hook β€” multi-turn conversations with streaming support, zero boilerplate
  • useAICompletion hook β€” single-shot completions for text generation, classification, summarization
  • RNAIMessage component β€” role-aware chat bubble with live streaming cursor
  • AI Chat screen β€” fully functional demo: provider switcher, API key input, model name input, streaming toggle
  • AI Guidance Files β€” CLAUDE.md, AGENTS.md, .cursor/rules/, .github/copilot-instructions.md so every AI coding assistant understands this codebase
  • @hooks path alias β€” clean imports for all custom hooks

πŸŽ‰ What's New in v5

  • React Native 0.84 + React 19 β€” latest stable versions
  • New Architecture enabled by default (newArchEnabled=true, Bridgeless / JSI)
  • Redesigned Boilerplate Explorer HomeScreen
  • Full Montserrat font family bundled (18 weights)
  • react-native-reanimated v4 + react-native-worklets
  • Stricter TypeScript config with typed theme colors and navigation
  • Husky v9 pre-commit hooks (prettier + lint on every commit, commitlint on commit message)

🐢 What's Included

AI Layer

Feature Description
AI Service Provider-agnostic sendAIMessage() + streamAIMessage() β€” works with OpenAI, Anthropic, Gemini, or any compatible API
useAIChat Multi-turn conversation hook with history, streaming, error state, and system prompt management
useAICompletion Single-shot completion hook β€” perfect for generation, summarization, translation, classification
RNAIMessage Role-aware chat bubble component (user / assistant / system) with streaming cursor
AI Chat Screen Live demo screen with provider selector, API key input, model name field, streaming toggle
AI Guidance Files CLAUDE.md Β· AGENTS.md Β· .cursor/rules/ Β· .github/copilot-instructions.md

Core

Feature Library
Navigation @react-navigation/native Β· @react-navigation/stack Β· @react-navigation/bottom-tabs
Navigation helpers react-navigation-helpers β€” push/pop/navigate without component refs
HTTP axios + axios-hooks
Animations react-native-reanimated v4 + react-native-gesture-handler
Icons react-native-vector-icons + react-native-dynamic-vector-icons
Localization i18next + react-i18next
Safe Area react-native-safe-area-context
Splash Screen react-native-splash-screen

Developer Experience

  • Strict TypeScript β€” extended theme types, typed palette, path aliases
  • Path aliases (@screens, @services, @hooks, @fonts, @theme, …)
  • TextWrapper β€” typed h1–h6, bold, italic props, global font swap in one file
  • EventEmitter singleton β€” pub/sub across screens without prop drilling
  • Montserrat font family (18 weights) bundled
  • ESLint + Prettier with auto import sorting
  • Husky pre-commit: lint, format, and commitlint run automatically

πŸš€ Getting Started

1. Clone

git clone https://github.com/kuraydev/react-native-typescript-boilerplate.git my-app
cd my-app

2. Install dependencies

npm install

3. iOS β€” install pods

cd ios && pod install && cd ..

4. Android β€” set SDK path

Create android/local.properties:

# macOS / Linux
sdk.dir=/Users/<your-username>/Library/Android/sdk

# Windows
sdk.dir=C:\\Users\\<your-username>\\AppData\\Local\\Android\\Sdk

5. Run

# iOS
npm run ios

# Android
npm run android

Scripts

Command Description
npm start Start Metro bundler
npm run start:fresh Start Metro with cache reset β€” use this after adding path aliases
npm run ios Run on iOS simulator
npm run android Run on Android emulator
npm run lint ESLint
npm run prettier Prettier β€” auto-format all files in src/
npm test Jest
npm run prepare Initialize Husky hooks (runs automatically after npm install)
npm run clean:showcase Remove showcase/demo content and replace screens with minimal stubs

Start fresh β€” remove showcase content

The included screens (Home, Search, Notifications, Settings) are demo UIs that showcase the architecture. Once you've explored the boilerplate, run:

npm run clean:showcase

This will:

  • Delete src/screens/home/mock/ (FeatureCards, UtilityItems, StackItems)
  • Delete src/screens/home/components/ (CardItem)
  • Replace each showcase screen with a minimal, compilable stub
  • Leave AIChatScreen, all shared components, services, theme, and navigation wiring untouched

Rename the project

npx react-native-rename <YourAppName>

For a custom Android bundle identifier:

npx react-native-rename <YourAppName> -b com.yourcompany.appname

For iOS, change the bundle identifier in Xcode.


πŸ“ Path Aliases

All aliases are defined in babel.config.js and tsconfig.json. Always prefer aliases over relative imports.

Alias Resolves to
@screens/* src/screens/*
@services/* src/services/*
@hooks src/hooks/index
@shared-components src/shared/components
@shared-constants src/shared/constants
@fonts src/shared/theme/fonts
@font-size src/shared/theme/font-size
@theme/* src/shared/theme/*
@colors src/shared/theme/colors
@models src/services/models
@utils src/utils
@assets src/assets
@event-emitter src/services/event-emitter
@api src/services/api/index (stub)
@local-storage src/services/local-storage (stub)

Example:

// Instead of: ../../../../shared/components/text-wrapper/TextWrapper
import Text from "@shared-components/text-wrapper/TextWrapper";

// Instead of: ../../hooks/useAIChat
import { useAIChat } from "@hooks";

After adding a new alias to both babel.config.js and tsconfig.json, run npm run start:fresh to reset Metro's cache.


πŸ€– AI Service Layer

The AI layer is provider-agnostic β€” the same API, types, and hooks work regardless of whether you're using OpenAI, Anthropic, Gemini, or any other LLM provider. Model names are never hardcoded; you supply whatever model string your API key supports.

Core types (src/services/ai/types.ts)

interface AIConfig {
  provider: "openai" | "anthropic" | "gemini";
  apiKey: string;
  model?: string;       // you supply the model β€” no defaults enforced
  temperature?: number; // defaults to 0.7
  maxTokens?: number;   // defaults to 1024
  systemPrompt?: string;
  baseURL?: string;     // override for proxies or local LLMs (e.g. Ollama)
}

interface AIMessage {
  id: string;
  role: "user" | "assistant" | "system";
  content: string;
  timestamp: number;
}

useAIChat β€” multi-turn conversations

import { useAIChat } from "@hooks";

const {
  messages,        // AIMessage[] β€” full conversation history
  isLoading,       // true while awaiting a full response
  isStreaming,     // true while tokens are arriving
  error,           // Error | null
  sendMessage,     // (content: string) => Promise<void> β€” full response
  streamMessage,   // (content: string) => Promise<void> β€” live tokens
  clearMessages,
  setSystemPrompt,
} = useAIChat({
  config: {
    provider: "openai",  // or "anthropic" or "gemini"
    apiKey: userApiKey,
    model: "your-model", // you decide β€” e.g. "gpt-4o-mini"
  },
});

// Full response β€” UI updates once reply is complete
await sendMessage("Explain React hooks in one paragraph");

// Streaming β€” the last assistant message updates token by token
await streamMessage("Write a React Native FlatList example");

// Inject a system instruction at any time
setSystemPrompt("You are a concise senior React Native engineer.");

useAICompletion β€” single-shot completions

import { useAICompletion } from "@hooks";

const { complete, result, isLoading, error, reset } = useAICompletion({
  config: { provider: "anthropic", apiKey: key, model: "your-model" },
  systemPrompt: "Classify sentiment as: positive, neutral, or negative.",
});

const sentiment = await complete(userReview);

Calling the service directly

import { sendAIMessage, streamAIMessage, buildUserMessage, buildSystemMessage } from "@services/ai";

const messages = [
  buildSystemMessage("You are a helpful assistant."),
  buildUserMessage("Hello!"),
];

// Full response
const response = await sendAIMessage(messages, config);
console.log(response.message.content);
console.log(response.usage?.totalTokens);

// Streaming
await streamAIMessage(messages, config, {
  onToken:    (token)    => appendToUI(token),
  onComplete: (response) => saveToHistory(response),
  onError:    (err)      => showErrorBanner(err.message),
});

Adding a new provider

  1. Create src/services/ai/providers/<name>.ts implementing IAIProvider:
import type { IAIProvider, AIConfig, AIMessage, AIChatResponse, AIStreamCallbacks } from "../types";

export class MyProvider implements IAIProvider {
  async sendMessage(messages: AIMessage[], config: AIConfig): Promise<AIChatResponse> {
    // call your API, return AIChatResponse
  }
  async streamMessage(messages: AIMessage[], config: AIConfig, callbacks: AIStreamCallbacks): Promise<void> {
    // stream tokens, call callbacks.onToken / onComplete / onError
  }
}
  1. Register in src/services/ai/AIService.ts:
case "<name>":
  return new MyProvider();
  1. Extend the union type in src/services/ai/types.ts:
export type AIProvider = "openai" | "anthropic" | "gemini" | "<name>";
  1. Add metadata:
export const AI_PROVIDER_LABELS: Record<AIProvider, string> = {
  // ...existing
  "<name>": "My Provider",
};

🎨 Theme System

The theme automatically follows the system dark/light mode via useColorScheme.

import { useTheme } from "@react-navigation/native";

const MyComponent = () => {
  const { colors } = useTheme();
  return <View style={{ backgroundColor: colors.background }} />;
};

Always use createStyles(theme) for style files

// MyComponent.style.ts
import { StyleSheet } from "react-native";
import type { ExtendedTheme } from "@react-navigation/native";

const createStyles = (theme: ExtendedTheme) => {
  const { colors } = theme;
  return StyleSheet.create({
    container: { backgroundColor: colors.card, borderColor: colors.borderColor },
  });
};

export default createStyles;

// MyComponent.tsx
const styles = useMemo(() => createStyles(theme), [theme]);

Palette reference

Token Light Dark
primary #4A6CF7 #4A6CF7
secondary #f97316 #f97316
background #f4f6fb #0e1117
text #1e2533 #e8edf5
placeholder #95a0b4 #6b7894
borderColor #e6eaf2 #2c313d
danger rgb(208,2,27) rgb(208,2,27)

TextWrapper

import Text from "@shared-components/text-wrapper/TextWrapper";
import fonts from "@fonts";

<Text h1 bold color={colors.text}>Title</Text>
<Text h4 color={colors.placeholder}>Subtitle</Text>
<Text fontFamily={fonts.montserrat.lightItalic}>Custom weight</Text>

🧭 Navigation

Stack wraps a Bottom Tab navigator. All screen name strings live in SCREENS (@shared-constants) β€” never use raw strings.

export const SCREENS = {
  ROOT: "Root",
  HOME: "Home",
  SEARCH: "Search",
  NOTIFICATION: "Notification",
  SETTINGS: "Settings",
  DETAIL: "Detail",
  AI_CHAT: "AIChat",
};

Navigate from anywhere

import * as NavigationService from "react-navigation-helpers";
import { SCREENS } from "@shared-constants";

NavigationService.push(SCREENS.DETAIL);
NavigationService.navigate(SCREENS.AI_CHAT);
NavigationService.pop();

Add a new screen

  1. Add key to SCREENS in src/shared/constants/index.ts
  2. Create src/screens/<name>/<Name>Screen.tsx + <Name>Screen.style.ts
  3. Register in src/navigation/index.tsx as <Tab.Screen> or <Stack.Screen>
  4. Add icon case in renderTabIcon if it's a tab

πŸ“‘ Event Emitter

A singleton EventEmitter at @event-emitter. Broadcast events between screens without prop drilling.

import EventEmitter from "@event-emitter";

// Emit
EventEmitter.emit("USER_LOGGED_IN", { userId: "abc123" });

// Listen (add on mount, remove on unmount)
useEffect(() => {
  const handler = (data: { userId: string }) => console.log(data.userId);
  EventEmitter.on("USER_LOGGED_IN", handler);
  return () => EventEmitter.off("USER_LOGGED_IN", handler);
}, []);

☁️ Axios Hooks

Declarative HTTP with built-in loading and error states.

import useAxios from "axios-hooks";

// GET
const [{ data, loading, error }, refetch] = useAxios("https://api.example.com/users");

// POST (manual)
const [{ loading }, executePost] = useAxios(
  { url: "https://api.example.com/users", method: "POST" },
  { manual: true },
);
await executePost({ data: { name: "John" } });

🌍 Localization

String tables in src/shared/localization/index.ts via i18next.

// Add translations
const resources = {
  en:    { translation: { greeting: "Hello!", logout: "Logout" } },
  tr:    { translation: { greeting: "Merhaba!", logout: "Γ‡Δ±kış" } },
};

// Use in components
import { useTranslation } from "react-i18next";
const { t } = useTranslation();
<Text>{t("greeting")}</Text>

// Switch locale
import i18n from "i18next";
i18n.changeLanguage("tr");

πŸ›  Utilities

import { capitalizeFirstLetter, generateRandomNumber } from "@utils";

capitalizeFirstLetter("hello");   // "Hello"
generateRandomNumber(1, 100);     // e.g. 42

🧠 AI Guidance Files

This boilerplate ships with a complete set of guidance files so every AI coding assistant understands the project conventions out of the box β€” and can help developers extend it correctly.

Top-level files

File Read by Content
CLAUDE.md Claude Code Full project reference: architecture, aliases, AI service docs, conventions, what to avoid
AGENTS.md Windsurf Β· Codex CLI Β· Gemini CLI Concise snapshot: layout, rules, extension guide

.cursor/rules/

Cursor reads these automatically when you're working inside the repo.

Rule file Content
project-conventions.mdc File naming, aliases, color rules, styles pattern, safe area, touchables
ai-service.mdc AI service architecture, hook usage, adding providers, streaming pattern
navigation.mdc SCREENS constant, adding tabs/stack screens, imperative navigation
components.mdc Creating shared components, TextWrapper/RNButton/RNInput usage, barrel exports
typescript.mdc Strict flags, I-prefix interfaces, export type, exhaustive switch, any policy

.github/

File Read by Content
copilot-instructions.md GitHub Copilot Workspace-level inline suggestion rules

These files are designed to be extended. As your project grows, update them to reflect new conventions, new services, or new architectural decisions β€” every AI tool in your team's workflow will stay aligned automatically.


βœ… Code Quality

ESLint + Prettier

# Lint check
npm run lint

# Auto-format all files in src/
npm run prettier

Config files: .eslintrc.js, .prettierrc, .eslintignore, .prettierignore

Husky v9 β€” pre-commit hooks

Husky runs automatically on every commit. No manual setup needed β€” npm install triggers prepare which initializes the hooks.

On every git commit:

  1. npm run prettier β€” auto-formats all staged source files
  2. npm run lint β€” ESLint must pass with no errors

On the commit message: 3. commitlint β€” validates the message follows Conventional Commits

Commit conventions (commitlint)

Commits must follow Conventional Commits:

feat: add user profile screen
fix: resolve dark mode flicker
chore: upgrade react-navigation to v7
docs: update README
refactor: extract AI provider logic
perf: memoize createStyles calls
test: add useAIChat unit tests

Allowed types: feat Β· fix Β· chore Β· docs Β· style Β· refactor Β· perf Β· test Β· ci Β· revert

Config file: .commitlintrc.json


πŸ“š Additional Documentation


πŸ—‚ Project Structure

src/
β”œβ”€β”€ assets/
β”‚   β”œβ”€β”€ fonts/
β”‚   β”‚   └── Montserrat/              # 18 font weights
β”‚   └── splash/
β”œβ”€β”€ hooks/                           # Custom React hooks
β”‚   β”œβ”€β”€ useAIChat.ts                 # Multi-turn AI conversation hook
β”‚   β”œβ”€β”€ useAICompletion.ts           # Single-shot AI completion hook
β”‚   └── index.ts
β”œβ”€β”€ navigation/
β”‚   └── index.tsx                    # Stack + Bottom Tab navigator
β”œβ”€β”€ screens/
β”‚   β”œβ”€β”€ ai-chat/                     # AI Chat demo screen
β”‚   β”‚   β”œβ”€β”€ AIChatScreen.tsx
β”‚   β”‚   └── AIChatScreen.style.ts
β”‚   β”œβ”€β”€ home/
β”‚   β”‚   β”œβ”€β”€ HomeScreen.tsx
β”‚   β”‚   └── HomeScreen.style.ts
β”‚   β”œβ”€β”€ detail/
β”‚   β”œβ”€β”€ notification/
β”‚   β”œβ”€β”€ search/
β”‚   └── settings/
β”œβ”€β”€ services/
β”‚   β”œβ”€β”€ ai/                          # Provider-agnostic AI service layer
β”‚   β”‚   β”œβ”€β”€ providers/
β”‚   β”‚   β”‚   β”œβ”€β”€ openai.ts            # OpenAI Chat Completions
β”‚   β”‚   β”‚   β”œβ”€β”€ anthropic.ts         # Anthropic Messages
β”‚   β”‚   β”‚   └── gemini.ts            # Google Gemini
β”‚   β”‚   β”œβ”€β”€ AIService.ts             # sendAIMessage, streamAIMessage
β”‚   β”‚   β”œβ”€β”€ types.ts                 # AIMessage, AIConfig, AIChatResponse…
β”‚   β”‚   └── index.ts
β”‚   β”œβ”€β”€ event-emitter/
β”‚   β”‚   └── index.ts                 # EventEmitter singleton
β”‚   └── models/
β”‚       └── index.ts                 # Shared TypeScript interfaces
β”œβ”€β”€ shared/
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ ai-message/              # RNAIMessage β€” chat bubble component
β”‚   β”‚   β”œβ”€β”€ badge/RNBadge.tsx
β”‚   β”‚   β”œβ”€β”€ button/RNButton.tsx
β”‚   β”‚   β”œβ”€β”€ divider/RNDivider.tsx
β”‚   β”‚   β”œβ”€β”€ empty-state/RNEmptyState.tsx
β”‚   β”‚   β”œβ”€β”€ input/RNInput.tsx
β”‚   β”‚   β”œβ”€β”€ loading-indicator/RNLoadingIndicator.tsx
β”‚   β”‚   β”œβ”€β”€ text-wrapper/TextWrapper.tsx
β”‚   β”‚   └── index.ts                 # Barrel export
β”‚   β”œβ”€β”€ constants/
β”‚   β”‚   └── index.ts                 # SCREENS enum
β”‚   β”œβ”€β”€ localization/
β”‚   β”‚   └── index.ts                 # i18next (en + tr-TR)
β”‚   └── theme/
β”‚       β”œβ”€β”€ colors.ts
β”‚       β”œβ”€β”€ font-size.ts
β”‚       β”œβ”€β”€ fonts.ts                 # Montserrat font map
β”‚       └── themes.ts                # LightTheme / DarkTheme + palette
└── utils/
    └── index.ts

Author

Kuray Β· kuraydev.io@gmail.com

License

MIT β€” see LICENSE for details.

About

πŸ€– AI-Ready React Native + TypeScript Boilerplate OpenAI Β· Anthropic Β· Gemini built in. New Architecture, streaming chat, typed themes, path aliases & more β€” production-ready from day one.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors