Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/KevxxAlva/tiktok-bot-downloader/llms.txt

Use this file to discover all available pages before exploring further.

TikTokSaver’s frontend lives entirely in the client/ package. It is a React 19 single-page application bundled by Vite 7, styled with Tailwind CSS v4, animated with Framer Motion v12, and routed with React Router v7. The entire build produces a static dist/ directory that Vercel serves directly, with all /api/* requests proxied to the Express serverless function.

Framework and toolchain

ToolVersionRole
React^19.2.4UI component model and state management
Vite^7.2.4Dev server, HMR, and production bundler
TypeScript~5.9.3Static typing across all source files
Tailwind CSS^4.1.18Utility-first styling via @tailwindcss/vite plugin
Framer Motion^12.33.0Entrance animations and AnimatePresence transitions
React Router^7.13.0Client-side routing (BrowserRouter)
Axios^1.13.4HTTP requests to the backend API
react-helmet-async^2.0.5Per-page <head> meta tag injection
react-ga4^2.1.0Google Analytics 4 pageview tracking
lucide-react^0.563.0Icon set (Waves, Download, Music, Mail, etc.)
Tailwind CSS v4 is loaded as a Vite plugin — no tailwind.config.js file is needed. The plugin is registered in vite.config.ts alongside the standard React plugin:
// client/vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        secure: false,
      }
    }
  }
})
The server.proxy configuration forwards every /api/* request to the local Express server during development, so the frontend never needs to know which environment it is running in.

Routing

App.tsx defines three client-side routes using BrowserRouter:
// client/src/App.tsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import PrivacyPolicy from './pages/PrivacyPolicy';
import TermsOfService from './pages/TermsOfService';
import GoogleAnalytics from './components/GoogleAnalytics';

function App() {
  return (
    <Router>
      <GoogleAnalytics />
      <Routes>
        <Route path="/"        element={<Home />} />
        <Route path="/privacy" element={<PrivacyPolicy />} />
        <Route path="/terms"   element={<TermsOfService />} />
      </Routes>
    </Router>
  );
}
GoogleAnalytics sits outside the <Routes> tree so it receives every route change and fires a GA4 pageview on each navigation.

Design system

All design tokens are declared as CSS custom properties in client/src/index.css and consumed throughout the stylesheet via var(). Tailwind utility classes reference these values when building component-level styles.
TokenValueUsage
--bg-dark#0e0e0ePage background
--surface-dark#1a1a1aCards, drawers, toggle backgrounds
--surface-highlight#252525Input field background
--accent-lime#ccff00Primary CTA buttons, active states, accent icons
--text-main#ffffffPrimary body text
--text-muted#888888Placeholder text, secondary labels
--border-color#333333All borders and dividers
The stylesheet also ships three reusable utility classes that are applied directly in JSX:
/* Minimal dark input */
.input-minimal { background: var(--surface-highlight); border: 1px solid var(--border-color); }
.input-minimal:focus { border-color: var(--accent-lime); background: #2a2a2a; }

/* Lime action button */
.btn-lime { background-color: var(--accent-lime); color: #000000; font-weight: 700; }
.btn-lime:hover { transform: translateY(-2px); box-shadow: 0 6px 25px rgba(204,255,0,0.3); }

/* Dark card surface */
.card-matte { background-color: var(--surface-dark); border: 1px solid var(--border-color); border-radius: 12px; }
The body receives a subtle radial gradient from #1a1a1a at the top to var(--bg-dark) further down, creating visual depth without a separate background component.

Component tree

Every page composes a small set of shared layout components. Below is a description of each: Header — A top navigation bar rendered by Home.tsx. Displays the TikTokSaver wordmark next to a lime Waves icon, a hidden-on-mobile “Cómo funciona” anchor link that scrolls to #Guia, and a Mail icon linking to the contact address. Footer — A bottom bar with copyright text, a contact mail icon, and React Router <Link> elements to /privacy and /terms. Includes a disclaimer stating the tool is not affiliated with TikTok or ByteDance. SeoHead — Wraps react-helmet-async’s <Helmet> to inject <title>, <meta name="description">, Open Graph tags, Twitter Card tags, and a canonical <link> for each page. Accepts title, description, keywords, image, and url props. VideoResult — The result card that appears after a successful API call. Detects whether the response contains images (slideshow) or a standard video. For slideshows it renders a 2-column grid of proxied thumbnail images, each with an individual download button. For videos it shows the proxied cover image with an overlay caption. All download buttons call window.location.href = /api/proxy-download?url=...&filename=... to trigger a native browser download. GoogleAnalytics — A render-less component that initializes the react-ga4 library with the VITE_GA_MEASUREMENT_ID environment variable on first mount and sends a pageview hit on every location change.

State in Home.tsx

The main page manages five pieces of state:
State variableTypePurpose
urlstringThe raw TikTok URL typed or pasted by the user
loadingbooleanToggles the spinner and disables the submit button
dataDownloadResult | nullPopulated on a successful API response
errorstringDisplayed in the error banner on API failure
mode'video' | 'audio'Drives the Video/Audio toggle UI (visual only)
The handleDownload function encodes the URL, calls axios.get('/api/download?url=...'), and either sets data or error depending on whether the response shape contains a result property.

TypeScript interfaces

The contract between the backend’s JSON response and the frontend’s rendering logic is expressed in client/src/types.ts.
Represents a single downloadable asset returned by the API — a clean video, watermarked video, or audio track.
export interface DownloadOption {
  type: 'hd' | 'normal' | 'music' | 'watermark';
  label: string;
  url: string;
  size?: number | string;
}
FieldDescription
typeAsset category — controls button color and icon in VideoResult
labelHuman-readable button label ("Sin Marca", "Con Marca (HD)", "Audio MP3")
urlAbsolute CDN URL, passed directly to /api/proxy-download
sizeFile size in bytes (optional); rendered as MB in the button label

Animations

Framer Motion is used in three places within Home.tsx and VideoResult.tsx:
  • Hero blockinitial={{ opacity: 0, y: -20 }} fades and slides the logo and headline in on mount.
  • Input areainitial={{ opacity: 0, scale: 0.95 }} with a delay: 0.2 scales the form in after the hero.
  • VideoResult card — wrapped in AnimatePresence so it animates in (opacity: 0, y: 30 → visible) when data is set and animates out when cleared.
The mode state variable ('video' | 'audio') drives the toggle button’s active styling but does not currently filter the download options returned by the API. The visual toggle is in place as a UI affordance for a future audio-only filter.

Build docs developers (and LLMs) love