ByteSize: Link Previews with Flask + React
Autogenerating beautiful link previews and embeds
In this Flask + React component tutorial we’re going to dig into building link previews. Link previews are commonly used in social networks as decoration to make conventional URLs more appealing than the typical blue underlined text.
If you wanted to learn how to make more cool components, be sure to check out the Infinite Grid with Unsplash tutorial as well!
And if you needed to find out how to create your Flask + React project from scratch, you can find a step by step guide here.
Getting Started
For this tutorial were going to be cheating a little bit by using a service called Microlink. Microlink provides OpenGraph scraping, link previews and embed components for free, as well as a host of other services. Rather than us trying to spend our times replicating it, our lives will be a lot simpler if we just use it ourselves.
There are two problems that we’ll have to deal with however:
- Their endpoint can be inconsistent and have a very high latency (sometime up to ~20 seconds!)
- If you are using the paid tier, you have to pass the API key on the front end, a big no-no for public facing applications.
We can navigate around these issues by using Python to make the request on the backend and returning the values. All we’ll have to do is make sure that the request is sent to our own backend rather than theirs.
For most intents and purposes, I tend to avoid rewriting packages. There can be a minefield of unintended and unforeseen consequences by making a shift, and you should try to avoid it yourself. In this case however, other than rewriting the entire thing from scratch, the switch should be rather simple.
I pinpointed the constant which holds the Microlink API endpoints, and replaced them with my own, as localhost (in app\static\node_modules\@microlink\mql\src\factory.js)
Now that the requests from the component are pointed to our own backend, we can create an endpoint that replicates their own.
Recreation
Recreating the endpoint is actually rather simple. Since the component uses the same API as the one we are requesting with Python, the results it return will be the exact same. In that way we won’t have to make any edits to the response and can just pass it through directly. (in app/main/views.py)
This endpoint retrieves the requested URL, which is passed as an argument with the request. We then create a dictionary for the other parameters that may be present. I created the iFrame entry second to show how you can make sure that the requests line up, but for my own purposes I know that I will always want at least the video and audio of the site.
As the call uses the requests library, be sure to install it
pip install requests
While I personally use the pro version (I make these components quite a bit), there is also a free tier with a limit of 50 calls per day. You will just need to change the “pro” subdomain of the request to “api” and remove the headers.
Now our endpoint will take the request from the Microlink component, and return all the data it needs in JSON format to be able to populate the fields.
Front End Component
Next up is creating our Front-End Component.
In order to allow a user to use the link preview, we have to allow an input of some kind. In the Infinite Grid tutorial we used a text field from Material-UI for our search bar, and we’ll make use of it again. You can install Material UI with:
npm i @material-ui/core
npm i @material-ui/icons
Our base component will look like so(in app/static/src/LinkPreview.jsx)
There’s quite a bit going on in this function already, so we’ll break it all down.
Our input field listens for changes with the onChange event, handling those changes with the handleChange function. handleChange then applies regex to determine whether or not the input contains what could be considered a URL. As URL’s come in all shapes and sizes (with very few people taking the time to enter ‘https://’, even though our API requires it), the function will append it. When that suitable match is found and formatted correctly, it’s set as the input variable. That value is then passed to the Microlink component, which fires a request to our backend and rendered on the page.
Bounce-Debounce
As you can imagine, there are ways that our user will end up firing more requests to the backend then we originally intend (.co, .com), so as with most user input that results in API calls, we should debounce our input and requests. We will create our own debounce hook that will wait a certain amount of time after the last input is detected (in app/static/src/debounce.jsx)
Then we will use this debounce hook in our LinkPreview component (in app/static/main/LinkPreview.jsx)
Why Pencils Have Erasers
People make mistakes.
Or maybe users decide they want to see more than one preview. Right now once the component is rendered, it will persist until another valid URL is found.
What would be preferable is that the preview would disappear once a user uses backspace on the input. We can use an onKeyUp event to monitor for that particular input (event.keycode === 8) and set our input to null. After the input is debounced, it will remove the preview (remember our conditional render of the Microlink component with !!debouncedQuery) (in app/static/src/LinkPreview.jsx)
Loading…
As a finishing touch, we should add a loading component between when input is received and when it has debounced (in app/static/src/LinkPreview.jsx)
Finished
And with that we’re done for now!
While we still have some work to do in increasing our request speed and increasing the functionality of the component (permitting multiple URLs, for example), we have the base of what we need.
As always thanks for reading, and be sure to follow and check back as next time we get into implementing some of those enhancements!