Build Discord-Style Rich Link Previews in React (Without CORS Issues)
Source: Dev.to
⚠️ Collection Error: Content refinement error: Error: 429 429 Too Many Requests: you (bkperio) have reached your weekly usage limit, upgrade for higher limits: https://ollama.com/upgrade
Have you ever tried to build a link preview card—like the ones you see in Discord, Twitter, or Slack—only to be instantly blocked by CORS errors? If you try to fetch a URL directly from your frontend React application to scrape its tags, the browser’s security model will stop you in your tracks. You have to do this from a backend. But instead of spinning up a whole Node.js/Puppeteer backend just to scrape OpenGraph tags, I’m going to show you how to do this in 5 minutes using a blazing-fast Rust-based utility API. We are going to build a React component that takes a URL and automatically renders a beautiful card with the site’s title, description, and preview image. To bypass CORS and extract the rich metadata, we’ll use the Core Web Utilities API on RapidAPI. Go to the Core Web Utilities API page. Subscribe to the Free tier. Grab your x-rapidapi-key. Here is the complete code for our LinkPreview component. We’ll use standard fetch to call our API and React’s useState and useEffect to manage the data. Pro Tip: Sometimes websites use relative paths for their images (like /favicon.ico instead of https://site.com/favicon.ico). In this component, we use the URL object to safely convert everything to absolute URLs, and we even fallback to Google’s hidden Favicon service if the site doesn’t have one! import React, { useState, useEffect } from ‘react’;
const LinkPreview = ({ url }) => { const [meta, setMeta] = useState(null); const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchMetadata = async () => {
setLoading(true);
try {
const response = await fetch(https://core-web-utilities-api.p.rapidapi.com/v1/metadata?url=${encodeURIComponent(url)}, {
method: ‘GET’,
headers: {
‘x-rapidapi-key’: ‘YOUR_RAPIDAPI_KEY_HERE’,
‘x-rapidapi-host’: ‘core-web-utilities-api.p.rapidapi.com’
}
});
const data = await response.json();
setMeta(data);
} catch (error) {
console.error("Failed to fetch metadata", error);
} finally {
setLoading(false);
}
};
if (url) fetchMetadata();
}, [url]);
if (loading) return ; if (!meta) return null;
// Helper to fix relative URLs (e.g. “/assets/image.png” -> “https://site.com/assets/image.png”) const getAbsoluteUrl = (path) => { if (!path) return null; try { return new URL(path, url).href; } catch { return path; } };
const domain = new URL(url).hostname; const imageUrl = getAbsoluteUrl(meta.image);
// Fallback to Google’s Favicon service if the site doesn’t provide one
const faviconUrl = getAbsoluteUrl(meta.favicon) || https://www.google.com/s2/favicons?domain=${domain}&sz=32;
return (
{/* Image Header with Fallback */}
{imageUrl ? (
) : (
No Preview Image
)}
{meta.title || meta.site_name || domain}
{meta.description && (
{meta.description}
)}
e.target.style.display = 'none'}
/>
{domain}
); };
export default LinkPreview;
Now you can drop this component anywhere in your app: export default function App() { return (
My Awesome Feed
); }
By offloading the scraping to the Core Web Utilities API, you get a few massive benefits: Zero CORS Issues: The API acts as a proxy, safely fetching the HTML from the server side. Deep Extraction: It automatically falls back between OpenGraph (og:title), Twitter Cards (twitter:title), and standard HTML tags so you always get the best result. No Heavy Dependencies: You don’t have to install heavy HTML parsers like cheerio or jsdom in your project. If you’re building a SaaS, a blog platform, or a chat app, rich link previews are a must-have for UX. Give the Core Web Utilities API a try for your next project, and let me know what you build!