Unfurl: how to display link previews, like Notion shows web bookmarks, with Next.js
Development
Check this out:
⬆️ This link preview box is pretty cool, isn’t it? ⬆️
It renders a link to my homepage, but pulling meta information from it: title, description, image (I don’t have) and favicon. If you have a website reading content from a CMS, like a blog, you might want to do this. I first saw it when using Notion, and then totally got into using link previews.
Unfurl
Unfurl, synonym of unfold, is defined by Oxford Languages Dictionary as “when something that is curled or rolled tightly unfurls, or you unfurl it, it opens”. It seems it was commonly used before, referring mostly to leaves or ship sails. But in recent years, this word has had a comeback.
Probably because we now use unfurl as a jargon verb referring to the process of obtaining additional information about a URL for the purpose of showing a preview.
And that’s what we’re gonna implement here.
Unfurling links with Next.js
Part 1. Unfurling a URL in an API route
With Next.js, we make an endpoint that takes the URL to unfurl, fetches its details, and returns them.
We’ll use the library unfurl.js to keep our code simple and avoid needing to traverse HTML ourselves.
We’ll also cache the response with the Cache-Control header, so that the CDN can cache the result and return it on subsequent calls right away, without unfurling the URL again.
For this example, we’re interested in the page meta title, meta description, favicon and social image, and we’ll return them in a response with this shape:
The route implementation is very simple! Check it out here:
You can view the full implementation for this website here: https://github.com/guillermodlpa/site/blob/2887fa4b6e1ba91c6b6ee64a8b740e8bf8331290/pages/api/bookmark/[url].ts
Part 2. The React component for link preview
Let’s make a component that requests the unfurled URL details, and renders the success, error and loading states.
Also, let’s separate the fetching into a custom hook, so our React component is more clean and maintainable.
The hook’s implementation can be very simple. In my case, just using useState
and Node fetch
:
And finally, you may implement each view as you see fit! Me, for this website, I’m using Chakra UI so I leverage its components like LinkBox, Skeleton, Flex, Text, etc.
You can check out my full implementation here: https://github.com/guillermodlpa/site/blob/2887fa4b6e1ba91c6b6ee64a8b740e8bf8331290/features/blogPost/NotionPageRenderer/components/BookmarkBlock.tsx
Cheers!