Let's build a simple web application that uses posts embedding code generation API exposed by the social networks.
TL;DR: In this chapter, you will learn about:
- Scaffolding a Svelte 3 app which performs a fetch request
- Adding Bootstrap 5 UI with using modern frontend techniques
- oEmbed format
- The very first step to build, deploy, and host your webapp on Azure Static Web Apps service
- The app will look like this: oEmbeddr v1
To find the app idea, I explored a couple of quite extensive unofficial TikTok API projects but decided to keep things simple and go for the only web-focused official one dedicated to embedding videos using oEmbed format. I've never heard about oEmbed before but it seems like it's an industry-standard in sharing the content from social networks: there are 248 providers available so far including all the major players.
This format and corresponding API is too simple - a single GET endpoint. So I decided to add some extra complications to challenge myself a bit: for the frontend I will use the Svelte framework I've never tried before, and for the UI, I will use Bootstrap. Well, it's hard to find a frontender who built websites in the early 10s (pre-frameworks golden era) and never used Bootstrap. I personally actively used versions 2 and 3 of it before switching to framework-connected UI tools. But after a long break, I decided to check what's going on with this legendary UI library and discovered a Bootstrap 5 Alpha 1 version announced in mid-June 2020. As blog post states, it's the first version without dependency on jQuery, including enhanced grid system, extensive usage of CSS variables, rearchitected form controls, and tons of other new features. It's hard to find a better opportunity to explore it and share my findings.
Also, TikTok video embedding API will be just a starting point for us - the app I called oEmbeddr will eventually support all the providers mentioned in the oEmbed repo. But let's keep this for the next articles. What do I want to achieve today:
- A SPA with only one section (so far) where we have the input for the social network post URL to share
- Sending a request to the corresponding API endpoint
- Displaying an HTML code we received, having "Copy to clipboard" feature, and a preview
- Responsive, accessible, satisfactory looking UI
- Zero configuration CI/CD workflow
- Globally distributed hosting
Let's go!
What is oEmbed and how to use it in TikTok
Many if not all social networks have the functionality to get an HTML code to embed a particular post in some external resource like a blogpost. And all of them build this feature using oEmbed format co-created by Cal Henderson, current Slack CTO, and co-founder.
From the UX perspective, some of the networks (like TikTok) have the corresponding button explicitly visible and getting the code is just a click away, other (like Facebook) redirect you to the developer portal and you have to learn about SDK, configuration, etc. before getting the code, thirds (like Twitter) just hide this feature from UI but support it.
You can read the full oEmbed format specification here but for our project, we'll use the simplest scenario: we call an API endpoint (GET method) where we pass a required parameter url
. In return, we receive a JSON with the HTML code in the html
field to embed. A page called Embed videos of the TikTok Developer portal informs us about supporting exactly this option.
And... we are lucky with selecting TikTok as a very first provider for our demo: it's the only service from the social networks I tried which has the Cross-origin resource sharing (CORS) configured the way we can call this API endpoint straight from the browser. So we can keep building backend of oEmbeddr for the next articles.
Scaffolding a Svelte app
Why did I ever decide to use Svelte? First, I wanted to try it for a long time. And second, I'm not a big fan of the direct DOM manipulation. So I'm fine with having some code overhead and getting a nice developer experience in return.
If you use the VS Code I strongly recommend installing Svelte extension. It provides syntax highlighting and rich intellisense for Svelte components in VS Code.
After reading the "Getting started" page I created a "hello world" template using degit - a utility for the straightforward project scaffolding, also by Rich Harris, creator of Svelte.
npx degit sveltejs/template oembeddr
cd oembeddr
npm install
npm run dev
Just a few moments and a ready-to-go dev environment created. Opening http://localhost:5000/
in the browser works fine. Running npm run build
generates a production code to be deployed in the public folder (let's remember this name for later).
After quick learning on how to bind data from the input value (to pass the URL user provides to my fetch method) and handling DOM events (form submission in my case), I was ready to fetch the data from TikTok API.
So, we have to send an HTTP request and update UI based on the response. This is where Svelte's reactivity and DX shine! I just followed this async/await example from the tutorial to have a resulting code and markup in the core (and only for now) app component in the file App.svelte like this:
And it works! Hereafter, I will use my first ever TikTok video as an example. Please feel free to watch the magic transition of my cat to Octocat, and follow me :)
I send a request, get results, display it in UI. All reactively, declaratively, without manual DOM manipulation. Svelte just works and the bundle.js size after I run npm run build
is 6K. I find it acceptable.
But we can't deploy a page with such a minimalistic design and poor UX. Let's use a UI library!
Adding Bootstrap 5
Let's live on the edge and install a very first (and only to the moment) Alpha of this version: npm install bootstrap@5.0.0-alpha1
Disclaimer. I'm aware of the awesome Sveltestrap library with some really "Svelte-native" components based on Bootstrap. It might be a better technical decision for real-life projects. But my strong intention was to try Bootstrap 5 (Sveltestrap uses v4) in a "modern frontend" mode: with compiling Sass for theming, with importing only JS components we use, with outsourcing the build to Svelte dev environment. Also, I understand that by direct usage of Bootstrap JS components I partially lost the app code's reactivity and declarativity.
Styles
The package contains both built/complied assets and source code. I want to have full control over resulting CSS both in terms of components I include and theme I use, so let's find a way to compile Sass during the Svelte app build.
Bootstrap uses PostCSS and Autoprefixer for Sass compilation and we can have these tools injected into the overall build flow by the magic of svelte-preprocess package that "acts as a facilitator to use other languages with Svelte, providing multiple features, sensible defaults, and less noisy development experience". To get this configured I follow this thread:
1) Installing the packages:
npm install -D svelte-preprocess node-sass autoprefixer
2) Adding to the Rollup configuration file rollup.config.js following lines:
import autoPreprocess from 'svelte-preprocess';
const preprocessOptions = {
scss: {
includePaths: [
'node_modules',
'src'
]
},
postcss: {
plugins: [
require('autoprefixer'),
]
}
}
export default {
...
plugins: [
svelte({
...
},
preprocess: autoPreprocess(preprocessOptions)
}),
...
],
...
};
It gives us possibility to use Sass syntax in the source code:
<style type="text/scss">
$body-bg: #eeeeee;
@import "bootstrap/scss/bootstrap";
// and other Sass awesome features
</style>
I also recommend to set up Svelte for VS Code extension to have better Sass support in IDE.
It's good to know that specifying node_modules
in includePaths
gives us the possibility to skip adding ../node_modules/ part of the import path which makes our source code look way cleaner.
Then, I create src/styles.scss file and follow the customization guide of Bootstrap. I import only the core configuration files and the files with the styles of components I will use: Nav, Navbar, Toast, Alert, etc. On top of the same file, I will override some color variables to give my app a unique style. The result (shortened a bit):
I reference this file in App.svelte:
<style type="text/scss">
@import 'styles';
</style>
Now, after the build, I see some Bootstrap classes in bundle.css - a good sign! But the file size is too good to be true - 2 to 7K (depending on the amount of HTML markup you have so far). Bootstrap CSS with the number of components we specified can't be so lean. When we try to apply some Bootstrap classes to HTML markup the CSS bundle file size grows but the result looks somehow broken. What's wrong? Nothing. In addition to styles scoping, Svelte's compiler is removing unused (in this particular component) classes from the resulting CSS. But in case of the UI library (like Bootstrap or others), we have some classes applied to outer parts of this particular component even if it's the root one (for example, for <body>
tag) and we want all classes to be available for all other components without scoping. I.e. we take a technical decision to plug in the whole UI library CSS file to Svelte's root component (App.svelte in our example) "as is".
And there is a solution for that! We can make the styles in the <style>
block fully or partially untouched by Svelte's scoping and optimizing engines by using a global
modifier like explained here. So I added it to the code:
<style global type="text/scss">
@import 'styles';
</style>
and everything related to Bootstrap styling started to work as intended.
The obvious downsides of this:
- Svelte can't optimize CSS by removing unused styles. We take care of it ourselves by importing only the parts of the UI library we need.
- Svelte can't prevent styles "leakage" from this component to all others. But in for the UI library, this behavior is exactly what we want.
All clear with the styles now, we are ready to use Bootstrap classes in our markup!
Adding some layout helpers, classes, extra attributes to the form...
...and the look & feel is completely different:
JavaScript plugins
Let's stop with the styles for now and have a look at Bootstrap's JavaScript plugins. For oEmbeddr, I decided to use:
- Tabbed content component (In the Bootstrap, it's called "Nav" in the component library, plus "Tab" in JavaScript plugins) to separate UI of the embed code (we'll place this code into an
textarea
) and preview (we'll load this code into aniframe
usingsrcdoc
attribute) - Floating message box ("Toast" in the Bootstrap) to show the success message after clicking the "Copy code to clipboard" button.
Setting up Nav/Tab is straightforward: just use the provided HTML code sample from the documentation with correct data attributes binding the "tab" and content it shows. For our scenario, there is no need in any JavaScript code except the module import:
import Tab from 'bootstrap/js/src/tab';
In the modern browsers, we can use this import without any extra processing in <script type="module">
tag but let's ask Svelte to do bundling for us, just put this import into <script>
section of App.svelte.
Let's cook a toast now. First, import:
import Toast from 'bootstrap/js/src/toast';
Then, adding HTML (I simplified this sample):
<div class="toast" role="alert" id="copySuccessToast" data-delay="2000">
<div class="toast-header">
<strong>oEmbeddr</strong>
</div>
<div class="toast-body">The code was copied to the clipboard</div>
</div>
Finally, we initialize and show (here is the API docs) this message box (after successful copying code from textarea to the clipboard):
let copySuccessToast = new Toast(document.getElementById("copySuccessToast"));
copySuccessToast.show();
Let's give it a try:
1) Open https://kind-sea-08852d11e.azurestaticapps.net/
2) Paste https://www.tiktok.com/@webmaxru/video/6858249402683886854 into the input, click "Get code"
3) Click "Copy to clipboard" and you will see this toast:
That was a journey! But now we know how to plug in Bootstrap 5 in its the most natural and flexible form to Svelte application.
Time to push our project to GitHub - here is my repo with the full source code, please, feel free to clone it - and proceed to the next step.
Hosting
Let's present our application to the whole world! With the global distribution and dynamic scale provided by Azure Static Web Apps service, we are fully covered!
This will be the shortest section of our story because setting up a frontend application on Azure Static Web Apps takes only a few clicks and less than 5 minutes.
Log in to Azure cloud portal (if you don't have an account please register a free trial), search for Static Web Apps in the top search field, and click "Add".
On the first screen, you enter your app name, resource group (or create new), and region. Also, you will be asked to log in to your GitHub account and to select an organization/repo/branch where the app located:
On the next screen, you need to specify some details about your project. In our case, we only need to type public in the App artifact location field (do you remember I asked you to remember where Svelte outputs the build artifacts?)
After clicking on "Create" and a few seconds, you will get a test public URL of your application available (for my project - https://kind-sea-08852d11e.azurestaticapps.net/). But while Azure Static Web Apps fetches your source code from GitHub, builds & deploys it (all automatically) you might want to have a look at the workflow for GitHub Actions file (my one) which was just created and added to your repo.
We are not going into the details of the Workflow file format, I just want to mention that since now, every push/merge to your master branch will invoke the CI/CD flow and you will be able to see an updated version of your app in a few moments (on the same URL). You can track the build progress in the corresponding GitHub Actions section.
Summary and what's next
We covered the application scaffolding using Svelte, adding Bootstrap 5 library, and hosted this simple page on the Azure Static Web Apps service. In the next article we:
- Check how the staging works in Azure Static Web Apps when we want to preview a next version of the application without touching the "production URL"
- Add more social network providers. To do it we'll build a backend using API part of the Azure Static Web Apps solution. Serverless for the win!
Let's stay connected on Twitter @webmaxru. And I'll be happy to see your feedback in the comments.
Maxim Salnikov
Developer Engagement Lead in Microsoft Norway