This article will show the approach to the creation of modern applications with the help of SSR-the framework Sapper.js.
To make the material understandable for developers with different levels of training, in our application we won't use TypeScript, HTML-template, custom fonts won't write tests and will not even configure the Linter.
Of course, in modern web development is a rare application do without, and if readers will have such interest, we will be able to deal with these topics in a separate article.
Teacher of IT courses, programming, computer science
Moscow school programmers, Moscow, Moscow oblast, St. Petersburg, 140 000 ₽
tproger.ru Jobs on tproger.ru
In this article, we focus on creating a simple application that will help to demonstrate the underlying principles of Sapper.
As REST API, we will use the JSON resource Placeholder, which is great for quick testing and prototyping.
For those who want to immediately see the final result:
Sapper.js high — level SSR framework based on Svelte.js that allows you to develop web applications, by abstracting the details of the distribution server and client code, so you can focus on application development.
The main advantages of using This:
About the benefits of the Sapper it is possible to speak long, it's the framework which is great for creating flexible and easily scalable applications, but offer to start and see all these benefits in action.
Despite the huge potential Sapper.js at the moment it is in beta stage and some things may change when he will reach the first stable release. For this article use the version of Sapper 0.28.10.
When you have finished the design process of application development is greatly accelerated, you don't have to think about what and how it should be. Even better when there is not just a design and UI kit.
This article was prepared the design of the application with minimal functionality is sufficient to demonstrate the capabilities of Sapper.js.
For development was used Figma, design and UI-kit, click here. You can copy this template and use it in your project.
Unfortunately, unlike older colleagues, such as Nuxt.js or Next.js, Sapper does not have a CLI-based utility for configuration template that you want.
The easiest way to start creating This application — copy to your computer the repository of the template with the utility degit. Let's do this:
npx degit "sveltejs/sapper-template#rollup" sapper-placeholder # or: npx degit "sveltejs/sapper-template#webpack" my-app
Note the creators of the Sapper offer us two variants of the starting template with different collector: rollup or webpack. Generally webpack is a more Mature solution, but in our case we will use the rollup, as it is easier to set up and for this project its capacity will be enough.
After downloading template, let's move into the newly created directory and install dependencies:
cd sapper-placeholder npm i
Now we can run our application using npm run dev. After that it will be available on localhost:3000.
By default, the starting template Sapper implemented the directory structure and files, suitable for quick start of development.
├ src │ ├ components │ │ ├ # Here components │ ├ routes │ │ ├ # Here routes │ │ ├ _layout.svelte │ │ ├ _error.svelte │ │ └ index.svelte │ ├ client.js │ ├ server.js │ ├ service-worker.js │ └ template.html ├ static │ ├ # Here images and other static ├ rollup.config.js └ package.json
For our project this structure fits perfectly, so we're not going to change it.
Read more about the purpose of different directories you can read in the repository template.
In this project we want to reuse styles defined in our UI kit, so we need the preprocessor, which will streamline our interaction with CSS.
A detailed guide to Sass/SCSS tproger.ru
In my projects I prefer the SCSS preprocessor, so let's set the rollup for its use.
The first thing we need to do is install the necessary packages:
npm i-D svelte-preprocess autoprefixer node-sass postcss
Then let's make changes in rollup.config.js.
The first thing we add to the beginning of the file import preprocessor:
import sveltePreprocess from 'svelte-preprocess';
Then let's create immediately after a block with the imports variable with the configuration of the SCSS to have the opportunity to refer to it from the server and the client blocks of the rollup options:
const preprocess = sveltePreprocess({ scss: { includePaths: ['src'], }, postcss: { plugins: [require('autoprefixer')], }, });
Now all we have to do is add a call to this variable in both blocks:
export default { client: { plugins: [ svelte({ // ... preprocess, // <-- Add here }), }, server: { plugins: [ svelte({ // ... preprocess, // <-- Here too }), ], }, };
Well, now we became available the use of the lang attribute to the style tag:
<style lang="scss"></style>
As in our project, all the existing styles described a single set of rules that facilitates the development, then let's just move them out of the Figma in the project files.
In the static directory, create a subdirectory styles, which will store all reusable styles in the project. In our case, we have a small project and we can have just three files:
Directory on GitHub.
Now we need to connect these variables and the classes in the project so that they can be accessed globally at any of our component. This requires to slightly change a variable in preprocess rollup.config.js:
const preprocess = sveltePreprocess({ scss: { includePaths: ['src', 'static/styles'], // <-- will Add to the array 'static/styles' prependData: `@import 'index.scss';`, // <-- Add this line }, postcss: { plugins: [require('autoprefixer')], }, });
As you can see, we just told our collector which file from which directory you want to connect globally at compile time.
The last thing we have to do is specify the global keyword in the style section of the _layout.svelte:
<style lang="scss" global></style>
However, if you now try to run our application, we will see in the console a lot of warnings that the global styles are specified, but not used. Looking ahead, I want to say that even after you start using these styles, these warnings disappear.
To solve this problem by slightly changing the configuration of a variable in onwarn rollup.config.js:
const onwarn = (warning, onwarn) => (warning.code !== "css-unused-selector") || <-- Add this line (warning.code === 'MISSING_EXPORT' && /'preload'/.test(warning.message)) || (warning.code === 'CIRCULAR_DEPENDENCY' && /[/\]@sapper[/\]/.test(warning.message)) || onwarn(warning);
Note Now in our console will not receive the incorrect message about unused global styles, but, unfortunately, it will also cease to receive the correct error about unused styles. The final decision to disable these warnings will have to make yourself.
Before you proceed to the next stage, let's also remove the global file.css, which was downloaded with the template are in the directory static and remove the link to download it from a file src/template.html.
Well, we are ready to move on.
Components are the building blocks, which will consist of our application.
Let's remove those components that were obtained from the starting template, and create your own.
Not to inflate article, I'm not here to bring the styles of components you can find them in the repository of this application.
Please note that when you create components and pages are used CSS utility classes, such as h1, body1, body2, medium that we created earlier in the file typography.scss.
The headers in our application are made in the same style, so it would be logical to display them use the same component and change the displayed data using the input parameters.
The component will have the following form:
the <script> export let title; export let subtitle; </script> <div class="ph"> <h1 class="h1 ph__title">{@html title}</h1> <p class="body2 ph__subtitle">{@html subtitle}</p> </div>
As we can see, this component is a simple wrapper for the data being displayed and not contain any logic.
Please note that for the interpolation data, we use a special tag @html, because in the future we will need to pass in the input parameters of not only text, but also links to other pages and resources that have snippets of HTML.
Component of the footer is very simple. It contains a link to the repository and conditional copyright.
Let's look at the template component:
<div class="pf body2"> <a href="https://github.com/mikhail-shpakov/sapper-placeholder" target="_blank" rel="noreferrer noopener"> Project on Github </a> <p class="body2 pf__subtitle"> {new Date().getUTCFullYear()} · Command to Timeweb Tproger.ru </p> </div>
Since this component contains no logic, the script section will be missing.
Information about each user will be drawn in its own component. Let's see how it will look like this component:
the <script> import UserCardInfo from "./UserCardInfo.svelte"; export let user; </script> <div class="uc"> <p class="uc__name body1 medium">{user.name}</p> <UserCardInfo icon="city" info={user.address.city}/> <UserCardInfo icon="site" info={user.website}/> <UserCardInfo icon="email" info={user.email}/> <a class="uc__link" href="/user/{user.id}"> View posts by </a> </div>
This component takes an input user parameter, which contains all the information about the user received from JSON Placeholder, and then just displays it in the template.
The card user we will have to show three icons: city, site and email. Let's create a subdirectory icons in the directory 'static' and add these icons by downloading them from our layout Figma.
You must have noticed that to display pieces of information we use component UserCardInfo, which is still not created. Let's fix that.
This component, in accordance with the design, shows a string with the user data and the appropriate icon, and is as follows:
the <script> export let icon; export let info; let iconPath = `icons/${icon}.svg` </script> <div class="uci"> <img src={iconPath} alt={icon} loading="lazy"> <p class="uci__info">{info}</p> </div>
Please note that img tag attribute loading that implements lazy loading of images, but currently has poor support in browsers.
Since this demo, we will implement a full crossbrowsers the work of lazy loading images, but in real applications it should be paid time.
Such functionality can be implemented as yourself, and to use already ready decision.
The last component that we have left to implement is a feature to display a specific post user.
It will look like the following:
the <script> export let post; </script> <div class="up"> <p class="up__title medium body1">{post.title}</p> <p class="up__body body2">{post.body}</p> </div>
Now that all our components ready, we can start to collect them from the page.
Our application will consist of the main page that lists all users and a dynamic page that will display posts of the selected user.
Let's remove those pages that were obtained from the starting template, and give the directory structure routes to the following form:
├ routes │ ├ user │ │ └ [id].svelte │ ├ _layout.svelte │ ├ _error.svelte │ └ index.svelte
As all our components are self-sufficient, and their status is defined through input parameters, our page will look as the list of components specified in the correct order.
To see the full page code on GitHub.
Let's start with the script section of the main page:
the <script> import {onMount} from 'svelte' PageHeader from import "../components/PageHeader.svelte"; UserCard from import "../components/UserCard.svelte"; let users = [] onMount(async () => { try { const res = await fetch(`https://jsonplaceholder.typicode.com/users`); users = await res.json(); } catch (e) { alert(`an error Occurred while loading user: ${e}`) } }) </script>
Look at what's going on here.
First, we initialize a variable users, which will store our list of users. Further in the onMount hook we make an AJAX request to get our users and write acquired data to users. To install the additional libraries that we use a native function to fetch.
Since our application is only a demo, in the event of failure on demand, we just show the user a message using alert. However, in real applications this is unlikely to be enough and will need to implement additional logic.
Now let's look at the pattern of our page:
<svelte:head> <title>First app on Sapper.js</title> </svelte:head> <PageHeader title="Sapper.js" subtitle="Create your first application using <a href='https://jsonplaceholder.typicode.com/' target='_blank' rel='noreferrer noopener'> JSON Placeholder </a>" /> <div class="users"> {#each users as user} <UserCard user={user}/> {/each} </div>
Here we first use the service tag <svelte:head> to set the title of the page, and then call the previously created components PageHeader and UserCard, passing data via input parameters.
When you call the component UserCard we use the Directive #each to iterate elements of array users.
The second page that we want to implement this page is a list of posts of the user. This page will have a dynamic parameter which will be user id.
In This dynamic parameters are specified using square brackets [...]. In our case, we want the posts of the user was accessible at the URL /user/[id] so we need to create the directory routes the user subdirectory and add a file named [id].svelte.
Let's look at the script section of this file:
<script context="module"> export async function preload({params}) { try { const res = await this.fetch(`https://jsonplaceholder.typicode.com/posts?userId=${params.id}`); const posts = await res.json(); return {posts} } catch (e) { this.error(e); } } </script> <script> import UserPost from "../../components/UserPost.svelte" import from PageHeader "../../components/PageHeader.svelte"; export let posts; </script>
As you can see, in this case we have two sections of script. The second should be all clear, as the familiar section. Here we just define the input parameter posts, which will get a list of posts.
For the first section of the script we added the context attribute="module", and inside the called function preload (read more here). This design allows us to execute the function call on the server to instantiate the component, and the obtained data to pass to the component as a input parameter.
In the browser we can use fetch to execute AJAX requests, but on server there are some limitations to this (read more here), so developers Sapper took care of that and provided us with a this function.fetch, which works similarly to fetch in the browser.
Another useful function for working with server-side developers Sapper is this.error. More about it can be read here.
Now let's look at the pattern of our page:
<svelte:head> <title>First app on Sapper.js</title> </svelte:head> <PageHeader title="All posts" subtitle="<a href='/'>back to main</a>"/> <div class="posts"> {#each posts as post} <UserPost post={post}/> {/each} </div>
Here, similar to how it is implemented on the main page, we first specify the title of the page, and then render the PageHeader and UserPost components, passing data via input parameters.
In the Sapper it is possible to create a component wrapper over the pages, which allows you to reuse common components between them and to implement the necessary custom logic.
Let's change obtained from the starting template file and bring it to the next view:
the <script> PageFooter from import "../components/PageFooter.svelte"; </script> <main> <slot/> <PageFooter/> </main>
As you can see, on all pages we'll automatically add a footer.
By default, any error returned from the server in the HTTP status Sapper redirects routes/_error.vue and passes the input parameters status and error description of the error.
In this application, we are satisfied with the logic and appearance of this page, so we're not going to change it.
Now, when our application is ready, it's time to do it deploom.
For this task I use Hostman cloud platform, which allows you to automate the process of deployment and make it easier.
Our app is a static website, as Hostman for static sites are available free rate without limits on performance, volume of traffic, or time of use.
All you have to do to publish is to click the interface button Create, choose free package and to connect our GitHub repository, putting the necessary options for deployment.
Immediately after that, automatically starts the publishing process and created a free domain in the zone *.hostman.site with an installed SSL certificate from Let's Encrypt.
And now with each new push in the selected branch (master by default) will also be deploying a new version of the application. Very easy and convenient.
In this article we discussed how to create a simple SSR application using Sapper.js and used only part of all its possibilities.
The framework is much more opportunities for the creation, organization and scaling of applications. But do not forget that at the moment it is still in beta and the decision about its use for developing real applications should be weighed carefully and justified.
Links to the final result:
1A Sportyvna sq, Kyiv, Ukraine 01023
804 NE 125th St, Miami, FL 33161
info@servreality.com
info@servreality.com