Mike HolpStart hereAll videos

Build guide · By · Updated

Build a YouTube feed in React that survives a failed request.

A creator homepage should still show useful videos when YouTube is slow. This site starts with a saved list of seven uploads, renders that list into HTML, and refreshes it in the browser.

What you’ll need

The saved example runs locally without buying a course or an AI subscription. Live hosting and provider usage may cost money; check the plans you choose.

Run the working example

git clone https://github.com/mikeholp87/ai-income-lab.git
cd ai-income-lab
npm ci
npm run dev

Open the local URL printed by Vite. The featured card should show a title, thumbnail, publication date, and a link to YouTube before you click Play. The browser player is deliberately loaded only after that click.

Follow the data

  1. src/youtube-snapshot.json holds the saved videos and channel counts.
  2. scripts/refresh-videos.js refreshes the snapshot at build time. If the public endpoint fails, a validated saved snapshot remains available.
  3. scripts/prerender.js renders the homepage into HTML. Video links are therefore available before browser JavaScript runs.
  4. useYouTubeFeed in src/main.jsx initializes from that same snapshot and asks /api/youtube for fresh data.
  5. api/youtube.js talks to YouTube and caches successful results. An API key, when used, stays on the server.

Keep a useful fallback

The essential React pattern is small. Initialize from data you already have, validate the replacement, and keep the saved list when refreshing fails:

const [feed, setFeed] = useState(savedFeed);

useEffect(() => {
  fetch('/api/youtube')
    .then(response => {
      if (!response.ok) throw new Error('Feed unavailable');
      return response.json();
    })
    .then(next => {
      if (validFeed(next)) setFeed(next);
    })
    .catch(() => {}); // The saved videos remain visible.
}, []);

Use the repository’s validFeed helper: it checks video IDs, dates, titles, summaries, and nonnegative view counts. Don’t place arbitrary API strings into HTML with innerHTML; render text through React.

Adapt it to your channel

Change channelUrl and channelId in src/youtube.js. Replace the saved JSON with your own public video data and point the refresh script at your own feed endpoint. The sample snapshot and refresh URL belong to AI Automation Station; changing only the visible channel name would leave the wrong videos in the build.

If you use a YouTube API key, configure YOUTUBE_API_KEY on the server. Do not prefix it with VITE_ or put it in the snapshot. Keep each card’s width and height defined, provide a useful accessible Play label, and size thumbnail downloads for the viewport.

Check the result

npm test
npm run build
npm run preview

Success means a readable, clickable feed survives a failed refresh. A cached snapshot is not a promise of real-time counts; this implementation refreshes live data daily.

When a check fails

If the local feed endpoint returns not found under Vite, the saved videos should still render: npm run dev does not run the Vercel API functions. If the build reports an invalid snapshot, replace it with a validated feed before retrying. If a fork shows this channel’s videos, update both the saved JSON and the refresh endpoint.

To check the pure data helpers without a network request, run node --test src/youtube.test.js. A passing test verifies the sample inputs; it does not prove that a third-party service is available.

Source and next step

Inspect the project source or see the feed on the homepage. Next, measure clicks after consent without treating a watch link as a subscription.