Build a professional Instagram and TikTok video downloader app using React Native Expo with the following specifications:
Name: VidSaver
Type: React Native Expo App (Development Build)
Purpose: Download HD videos from Instagram and TikTok without watermarks\
- Expo SDK: ~54.0.27
- React: 19.1.0
- React Native: 0.81.5
- Build Type: Development build (not Expo Go)
- Navigation: Expo Router (~6.0.17)
- TypeScript: ~5.9.2
- NativeWind: ^4.2.1 (Tailwind CSS for React Native)
- Tailwind CSS: ^3.4.18
- Design: Modern, clean, premium UI
"expo-router": "~6.0.17",
"expo-file-system": "^19.0.20",
"expo-media-library": "^18.2.1",
"expo-secure-store": "^15.0.8",
"expo-status-bar": "~3.0.9",
"expo-linking": "~8.0.10",
"expo-splash-screen": "~31.0.12",
"expo-constants": "~18.0.11",
"expo-image": "~3.0.11""@react-navigation/native": "^7.1.8",
"@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0""@expo/vector-icons": "^15.0.3""axios": "^1.13.2"Backend: Custom FastAPI Backend
URL: https://fastapi-u8bm.onrender.com
Endpoints:
- Metadata:
POST /api/metadata- Body:
{ "url": "https://..." } - Returns:
{ "success": true, "data": { "title": "...", "thumbnail": "...", ... } }
- Body:
- Direct URL:
POST /api/get-direct-url- Body:
{ "url": "https://..." } - Returns:
{ "success": true, "data": { "direct_url": "...", "filename": "..." } }
- Body:
No external API keys required. The app communicates directly with this self-hosted backend.
Query Parameters:
url(required): Full TikTok video URL (URL-encoded)hd=1: Request HD quality video
Response Structure:
{
code: 0, // 0 = success, other = error
msg: string,
data: {
title: string,
cover: string, // Thumbnail URL
origin_cover: string, // Alternative thumbnail
hdplay: string, // HD video URL (preferred)
play: string, // Standard quality URL
wmplay: string, // Watermarked video URL (fallback)
author: {
nickname: string,
unique_id: string
}
}
}Implementation Details:
- Encode full TikTok URL and append to API endpoint
- Parse response to check
code === 0for success - Extract metadata from
dataobject - Select download URL with priority:
hdplay>play>wmplay
Download URL Priority: hdplay (HD) > play (standard) > wmplay (with watermark)
Supported URL Formats:
- Standard:
https://www.tiktok.com/@username/video/1234567890 - Short links:
https://vm.tiktok.com/SHORTCODE/
Primary Brand: Gradient (#6366f1 to #a855f7) - Indigo to Purple
Primary Color: #6366f1 (Indigo-500)
Primary Variants:
- primary-600:
#4f46e5 - primary-700:
#4338ca - primary-100:
#e0e7ff
Status Bar:
- Background:
#6366f1(matches header top) - Style: light content (white text)
Background:
- Main:
#f9fafb(gray-50) - Cards:
#ffffff(white) with shadow-md
Layout:
- Header with app name "Vid-Saver" on top left
- Video URL input field (auto-paste from clipboard functionality)
- Video metadata card showing:
- Thumbnail
- Title
- Author
- Platform badge (Instagram/TikTok)
- Download progress bar with percentage
- Automatic download after metadata loads
- Retry button on download failure
Icons Used:
import Feather from '@expo/vector-icons/Feather';
<Feather name="home" size={28} color="black" />Layout:
- List view of downloaded videos
- Each item shows:
- Video thumbnail
- Title
- Platform badge
- Download date
- File size
- Delete button (trash-2 icon)
- Empty state with download icon
Icons Used:
<Feather name="download" size={24} color="black" />
<Feather name="trash-2" size={20} color="#ef4444" />Style: Modern Docked Floating Bar
- Appearance: Rounded top corners (
rounded-t-[25px]), absolute positioning bottom. - Background: Linear Gradient (
#6366f1->#a855f7). - Icons: White, optimized size (28px) with visual centering.
Tabs:
- Home -
<Feather name="home" /> - Downloads -
<Feather name="download" />
Support patterns:
- Instagram:
/p/,/reel/,/tv/ - TikTok:
tiktok.com,vm.tiktok.com
Scheme: vidsaver
Intent Filter: Accepts text/plain from Instagram/TikTok share menu
Behavior: Auto-download video when shared from external app
- User pastes/shares video URL
- Extract metadata (thumbnail, title, author)
- Auto-download starts
- Show progress (0-100%)
- Save to device gallery
- Store metadata in secure store
- Show success message
Video Storage: Storage Access Framework (SAF)
- User selects "Downloads" folder one time.
- App auto-creates
.../Downloads/VidSaver/subfolder. - Videos saved directly to this folder (no duplicates).
Metadata Storage: Expo SecureStore (encrypted)
File System: expo-file-system (standard)
Mechanism: Alert confirmation dialog "Are you sure?" before deletion.
Android:
"android.permission.INTERNET",
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_MEDIA_VIDEO",
"android.permission.READ_MEDIA_IMAGES"iOS:
"NSPhotoLibraryUsageDescription": "Save downloaded videos",
"NSPhotoLibraryAddUsageDescription": "Permission to save videos"Permission Request: On app startup (in _layout.tsx)
app/
├── _layout.tsx # Root layout with permissions
├── (tabs)/
│ ├── _layout.tsx # Tab navigation
│ ├── index.tsx # Home screen
│ └── downloads.tsx # Downloads screen
components/
├── Header.tsx # App header
├── VideoInput.tsx # URL input field
├── VideoMetadataCard.tsx # Video preview card
├── DownloadProgress.tsx # Progress bar
└── VideoListItem.tsx # Downloaded video item
services/
├── videoDownloader.ts # API calls & download logic
└── storage.ts # MediaLibrary & SecureStore
utils/
└── urlValidator.ts # URL validation & platform detection
types/
└── index.ts # TypeScript interfaces
Location: services/videoDownloader.ts
Fetches video metadata (thumbnail, title, author) from Instagram or TikTok.
Parameters:
url: Full video URL (Instagram or TikTok)
Returns:
{
url: string,
title: string,
thumbnail: string,
platform: 'instagram' | 'tiktok',
author: string
}Process:
- Detects platform using
getPlatformFromUrl() - For Instagram: Extracts shortcode and calls RapidAPI
- For TikTok: Calls TikWM API with full URL
- Parses response and returns normalized metadata
downloadVideo(videoUrl: string, metadata: VideoMetadata, onProgress?: (progress: DownloadProgress) => void): Promise
Downloads video file with progress tracking.
Parameters:
videoUrl: Video URL to downloadmetadata: Video metadata from fetchVideoMetadataonProgress: Optional callback for download progress
Returns: Local file URI of downloaded video
Process:
- Fetches video download URL from appropriate API
- For Instagram: Uses
contents[0].videos[0].url - For TikTok: Uses
hdplay>play>wmplay - Downloads with
FileSystem.createDownloadResumable() - Tracks progress and calls onProgress callback
- Returns local file URI upon completion
Progress Object:
{
totalBytes: number,
downloadedBytes: number,
percentage: number
}Location: services/storage.ts
Saves downloaded video to device gallery and stores metadata.
Parameters:
localUri: Local file URI from downloadVideometadata: Video metadata
Returns:
{
id: string, // Media library asset ID
metadata: VideoMetadata,
localUri: string, // Gallery URI
downloadedAt: number, // Timestamp
fileSize: number // File size in bytes
}Process:
- Checks MediaLibrary permissions
- Creates asset in gallery (no album to avoid permission dialogs)
- Gets file size
- Stores metadata in SecureStore
- Returns DownloadedVideo object
Retrieves list of all downloaded videos from SecureStore.
Returns: Array of DownloadedVideo objects
Storage: Uses SecureStore with key 'downloaded_videos'
Deletes video from gallery and removes metadata.
Parameters:
videoId: Media library asset ID
Process:
- Deletes from MediaLibrary using asset ID
- Removes from SecureStore metadata list
Formats file size for human-readable display.
Returns: Formatted string (e.g., "15.2 MB", "1.5 GB")
- Modern: Use rounded corners, shadows, gradients
- Clean: Ample white space, clear hierarchy
- Premium: Smooth animations, quality interactions
- Responsive: Handle all screen sizes
// Cards
className="bg-white rounded-xl shadow-md p-4"
// Buttons
className="bg-primary-600 rounded-xl py-4 shadow-lg"
// Input
className="bg-white border border-gray-300 rounded-xl px-4 py-3"
// Badges
className="bg-primary-100 px-2 py-0.5 rounded-full"- Headings: font-bold, text-xl+
- Body: text-gray-600, text-sm/base
- Labels: text-xs, uppercase
Adaptive Icon:
- Background:
#E6F4FE - Foreground:
./assets/images/android-icon-foreground.png - Monochrome: Supported
Features:
- Edge-to-edge: Enabled
- New Architecture: Enabled
- Status bar color:
#6366f1
-
No Album Creation: Save videos directly to gallery (avoid permission dialogs)
-
Error Handling: Show retry button on download failure with persistent error message
-
Progress Tracking: Use FileSystem.createDownloadResumable with progress callback
-
Auto-Download: Automatically start download after metadata loads (no manual button in normal flow)
-
Icon Library: Use Feather icons from @expo/vector-icons exclusively
-
Status Bar: Use expo-status-bar (not react-native StatusBar)
-
Legacy File System: Import from 'expo-file-system/legacy'
- Test Instagram posts, reels, and TV videos
- Test TikTok regular and short videos
- Test share intent from both platforms
- Verify no permission dialogs after initial setup
- Test retry functionality on network failure
- Verify videos appear in device gallery
✅ Downloads HD videos without watermarks
✅ Supports Instagram and TikTok
✅ Clean, modern UI
✅ Share intent integration works
✅ Auto-download on URL paste
✅ Progress tracking
✅ Persistent downloaded videos list
✅ No repetitive permission dialogs
✅ Error handling with retry option