The best AniList experience on mobile. Not a web wrapper, not an afterthought β a native app built with modern React Native.
A feature-rich mobile application for AniList users to browse, search, and track anime and manga with a beautiful, intuitive interface.
- Home & Trending - Discover trending and popular anime and manga
- Search - Advanced search functionality for finding your favorite titles
- Library - View and manage your personal anime and manga collection
- Profile - Check your statistics, favorite characters, staff, and studios
- Forum - Community discussions and interactions
- Beautiful UI - Dark mode optimized, smooth animations, and responsive design
- OAuth Integration - Seamless AniList authentication
Download the latest APK build directly on your Android device:
- Go to Releases
- Download the latest
.apkfile - Open the file on your Android device and tap Install
- Grant necessary permissions when prompted
- Framework: React Native with Expo
- Language: TypeScript
- Navigation: Expo Router
- GraphQL Client: URQL
- GraphQL Code Generation: GraphQL Code Generator
- Styling: NativeWind (Tailwind CSS for React Native)
- State Management: Zustand
- Storage: react-native-mmkv
- Code Quality: ESLint, Prettier, Husky, Commitlint
- UI Components: Lucide React Native, expo-image, FlashList
- Node.js (v18 or higher)
- npm or yarn
- Expo CLI:
npm install -g expo-cli - Android Studio (for Android development) or Xcode (for iOS development)
- AniList API access (handled through OAuth)
git clone https://github.com/monsurcodes/kaiwa.git
cd kaiwanpm installCreate a .env file based on .env.example:
cp .env.example .envUpdate the .env file with your configuration values. Key variables:
- AniList API endpoint (pre-configured)
- Any additional authentication tokens if needed
Before running the app, generate TypeScript types from GraphQL queries:
npm run codegenThis command:
- Reads your GraphQL queries from
lib/graphql/queries/and mutations - Generates strongly-typed TypeScript interfaces in
lib/graphql/generated/ - Ensures type safety across your GraphQL operations
npm run androidOr with Expo CLI:
expo start
# Press 'a' to open Androidnpm run ios# Development
npm start # Start Expo dev server
npm run android # Run on Android emulator/device
npm run ios # Run on iOS simulator/device
npm run web # Run on web
# Code Quality
npm run format # Format code with Prettier
npm run format:check # Check formatting without changing
npm run lint # Run ESLint
npm run lint:fix # Fix ESLint errors automatically
# GraphQL
npm run codegen # Generate GraphQL types
# Git Hooks
npm run prepare # Setup Husky git hooksThe app uses AniList OAuth 2.0 for authentication:
- Users tap the login button
- Browser opens AniList authorization page
- After approval, callback redirects back to the app
- Access token is stored securely using
react-native-mmkv - All subsequent API requests include the token via URQL auth exchange
All GraphQL queries are located in lib/graphql/queries/. To add a new query:
- Create a new file:
lib/graphql/queries/myQuery.ts - Define your query using the
gqlfunction:
import { gql } from "@/lib/graphql/generated";
export const MyQuery = gql(/* GraphQL */ `
query GetMyData($id: Int!) {
Media(id: $id) {
id
title {
romaji
}
}
}
`);- Run
npm run codegento generate types - Use the query in your component with
useQuery
This project uses NativeWind (Tailwind CSS for React Native). Key points:
- Classes are applied via
classNameprop on React Native components - Responsive classes work similarly to web Tailwind
- Dark mode is configured in
tailwind.config.js - Custom colors are defined in theme configuration
Example:
<View className="flex-1 items-center justify-center bg-slate-900">
<Text className="text-lg font-semibold text-white">Hello</Text>
</View>Zustand is used for global state management. Example:
// In stores/authStore.ts
import { create } from "zustand";
interface AuthState {
token: string | null;
setToken: (token: string) => void;
}
export const useAuthStore = create<AuthState>((set) => ({
token: null,
setToken: (token) => set({ token }),
}));
// In a component
const token = useAuthStore((state) => state.token);The app uses react-native-mmkv for fast, encrypted local storage:
import { MMKV } from "react-native-mmkv";
const storage = new MMKV();
storage.setString("key", "value");
const value = storage.getString("key");- Formatting: Prettier (run
npm run formatbefore committing) - Linting: ESLint with Expo config
- Commits: Commitlint enforces conventional commits via Husky
- Pre-commit Hooks: Automatically format and lint staged files
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes and ensure code quality:
npm run lint:fix npm run format
- Commit with a conventional message:
git commit -m "feat: add new feature" - Push and create a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
expo start -c # Clear cache and restartnpm run codegen # Regenerate types after modifying queriesEnsure you have the latest Android SDK and proper environment variables set in your Android Studio configuration.
- Clear node_modules and reinstall:
rm -rf node_modules && npm install - Rebuild Expo:
expo prebuild --clean
For issues, questions, or contributions, please open an issue or pull request on GitHub.
Made with β€οΈ for AniList enthusiasts