Last active
March 2, 2024 10:20
-
-
Save ajitid/ecaaf5ee878d3d78b7bbb0aa88ac9e72 to your computer and use it in GitHub Desktop.
Okay-ish way get title and favicon on the browser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState } from "react"; | |
import ky from "ky"; | |
import cheerio from "cheerio"; | |
function App() { | |
const [url, setUrl] = useState(""); | |
const [title, setTitle] = useState(""); | |
const [faviconUrl, setFaviconUrl] = useState(""); | |
const handleSubmit = async () => { | |
try { | |
const html = await ky | |
// .get(`https://cors-anywhere.herokuapp.com/${url}`) | |
.get(`https://bookmarks-cors-anywhere.herokuapp.com/${url}`) | |
.text(); | |
const $ = cheerio.load(html); | |
// title of webpage | |
const extractedTitle = $("title").text(); | |
setTitle(extractedTitle ? extractedTitle : url); | |
// We'll first try to find favicon in parsed html, if we cannot find it there | |
// then we'll fallback to its where it is usually present (/favicon.ico). | |
// Favicons are usually not behind CORS. | |
let extractedIconPath = | |
$('link[rel="icon"]').prop("href") ?? | |
$('link[rel="shortcut icon"]').prop("href") ?? | |
"favicon.ico"; | |
const parsedUrl = new URL(url); | |
if (extractedIconPath.startsWith("http")) { | |
setFaviconUrl(extractedIconPath); | |
} else if (extractedIconPath.startsWith("//")) { | |
setFaviconUrl(`${parsedUrl.protocol}${extractedIconPath}`); | |
} else { | |
const iconPathname = extractedIconPath.startsWith("/") | |
? extractedIconPath | |
: `/${extractedIconPath}`; | |
setFaviconUrl(`${parsedUrl.origin}${iconPathname}`); | |
} | |
} catch (error) { | |
console.log({ error }); | |
} | |
}; | |
return ( | |
<div> | |
<form | |
onSubmit={(e) => { | |
e.preventDefault(); | |
handleSubmit(); | |
}} | |
> | |
<input | |
type="text" | |
value={url} | |
onChange={(e) => setUrl(e.target.value)} | |
/> | |
<button>Sumbit</button> | |
</form> | |
<img src={faviconUrl} alt={title} /> | |
<div>{title}</div> | |
</div> | |
); | |
} | |
export default App; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Copied from a raised issue after merging this code
We can start by displaying title and favicon. Because of CORS limitation on frontend we'll need a backend server. An easy way is to use CORS Anywhere. Its demo page has really high rate limits so I've deployed it somewhere else to have less restrictions. Mind you, it is a temporary solution and is only for development purposes.
This is still not a perfect solution. With pages being loaded client-side, title and favicon both might be missing or could be incorrect. (For eg. title in Twitter)
To explore: