Logo
    Create your first app on Sapper.js
    • news
    • -
    • Create your first app on Sapper.js

    Create your first app on Sapper.js

    Michael Shpakov

    lead frontend developer at Timeweb

    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:

    • Github
    • Design
    • Demo

    A little bit about Sapper.js

    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:

    • SSR and PreRender are already configured, all you need to do is choose the desired mode at startup.
    • Excellent SEO for all search engines, as a result of the use of SSR or PreRender.
    • A quick interaction with the site, in comparison with static websites, by loading only the necessary js chunks, css styles and API queries (a big part of this process automatiseret webpack or rollup) and by optimizing the application at compile time.
    • Excellent performance Google Lighthouse/Page Speed when properly configured, the ability to get 100/100, even on slower server.
    • The ability to use any packages from the ecosystem Svelte.js.

    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.

    Design

    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.

    The creation of the 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.

    The structure of the project

    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.

    Configure SCSS

    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>

    Reusable styles

    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:

    1. variables.scss — to contain all the SCSS-variables used in the project.
    2. typography.scss will contain all of the classes helpers for text and links. Please note that these helper classes to change styles depending on the resolution used by the user device: mobile or PC.
    3. index.scss — shared file, which will be used as a single point of export all global and class variables.

    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

    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.

    PageHeader

    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.

    PageFooter

    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.

    UserCard

    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.

    UserCardInfo

    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.

    UserPost

    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.

    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.

    Main page with list of users

    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 page with the list of posts of user

    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.

    _layout.svelte

    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.

    _error.svelte

    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.

    Deploy to Hostman

    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.

    Conclusion

    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:

    • Github
    • Design
    • Demo

    CONTACTS

    Address

    1A Sportyvna sq, Kyiv, Ukraine 01023

    804 NE 125th St, Miami, FL 33161

    Email

    info@servreality.com

    Skype

    info@servreality.com

    arrow-btn