Learn Svelte 3 by building a Todo List App
A practical introduction to Svelte 3 by building a simple Todo list app
In this article, we’ll pick up from the previous post that introduced the Svelte 3 library and tackle another exercise that teaches a little more about the features Svelte has to offer.
During the course of working through this tutorial, you will build an application that searches Unsplash and presents the results in an infinitly scrolling list.
Here’s what the completed project will look like:
This tutorial assumes you have a little bit of prior experience with Svelte 3. It should also be easy enough to follow if you are experienced with React, Vue or other similar JavaScript libraries.
Otherwise, you might want to jump in to Svelte 3 with this introductory tutorial that covers building a todo list application before coming back to this one.
Clone this GitHub repository to your computer and cd
into the created directory. Then run npm install
from the project root to install all the dependencies specified in the package.json
file.
After installing the dependencies, start the development server by running npm run dev
which should compile the app and make it accessible at http://localhost:5000.
Create a new Search.svelte
file in the src
folder of your root directory and add the following code into it:
The way to declare props in Svelte 3 is by using the export
keyword on a variable declaration. In the above case, we’re expecting a query
prop which is bound to the <input>
element, and handleSubmit
which is a function called when .search-form
is submitted.
Let’s go ahead and create these two properties in the App
component while passing them down to Search
.
Update your src/App.svelte
file as shown below:
The searchQuery
variable is bound to the query
prop with the help of the bind
keyword. This bind:property={value}
syntax is what allows data to flow from child component to parent component.
On the other hand, the handleSubmit
prop points to the handleSubmit
function created above. After checking if the searchTerm
is not an empty string, the searchUnsplash()
function is invoked which sends the query to the Unsplash API and appends the results to the searchResults
array.
To use the Unsplash API, you need to create a free account on their website first. Follow the instructions on this page to do so, and register a new application. Once your app is created, copy the access key and paste it in place of <your unsplash access key>
above.
As seen from the above section, the searchResults
array holds incoming results from the Unsplash API. What we want to do is render an image card for each result in the array. To do so, first create a SearchResults.svelte
component in the src
directory as follows:
Then create the PhotoCard
component in the same directory as well:
In SearchResults
, you can see that the PhotoCard
component is rendered for each object in the array. Pretty straightforward to reason about if you ask me.
The final step is to import SearchResults
in App.svelte
and pass it the searchResults
array as props.
Test it out by searching for a generic word. It should render a list of results similar to the GIF below:
When a search request is ongoing, we want to show a loading indicator in the app so that users can know that the search is currently in progress.
Create a new LoadingIndicator.svelte
file within the src
folder, and populate it with the following code:
Next, let’s import LoadingIndicator
in App.svelte
and conditionally render it when isLoading
is true
:
At this point, the loading indicator should display in the brief interlude between when a query is made and when the results are returned.
At the moment, only one page of results is being loaded for a search query. Each page contains 28 photos according to what we specified in the per_page
query parameter in the endpoint
.
We can load more pages by increasing the value of nextPage
in subsequent requests as long it doesn’t exceed the total number of pages available for a particular search term.
A standard way to load more pages would be to have a “More results” button of some sort that the user has to click to load the next page into view. Another common approach is to have some pagination so that the user can jump to a specific page.
I’m going to show you a different way that is similar to how it’s done on the Unsplash website. As the user scrolls to the bottom of the page, the next page is appended to the view automatically. If you’re thinking “Infinite scrolling”, you’ve got that right! We’ll make use of the Intersection Observer API to achieve this.
The basic idea behind the Intersection Observer API is that it allows you to react to an element scrolling into view on a webpage. To use it, you need to create an observer instance and attach it to a root element, usually the browser viewport. Then you request this observer to monitor a child of the root element and execute a callback function when the element scrolls into view.
Truth be told, it’s a little more nuanced than that, and while I’m unable to provide an in-depth explanation for how the API works in this article, you can always investigate on your own. The MDN resouce linked above is a good place to start.
Jumping back to our app, we’re going to make use of Svelte’s onMount
lifecycle hook so that we can create the observer when the App
component is first rendered to the DOM.
First, import onMount
from the svelte
package above the other imports in App.svelte
:
Then place the following chunk of code above the handleSubmit
function:
The code above basically calls loadMoreResults
when the .loading-indicator
element is about 300px below the viewport. If the conditions are satisfied, searchUnsplash
is called again and the next page is added to the searchResults
array which triggers an update to the view.
To wrap up, we’ll unobserve the target
element once all the pages for a search have been loaded into view. This prevents unnecessary requests to Unsplash’s API after all the pages for a search query has been loaded into view.
We can easily do this in the finally
method in searchUnsplash
:
The problem this creates is that if the target is unobserved and a new search is performed, only the first page of results will be loaded by virtue of the target
being unobserved.
We can fix this by observing the target
element every time a new search is performed rather than just when the component is mounted. This ensures that even if target
is unobserved at on the last search page, it will be observed again for a new search query.
Move the following line from the onMount
method to handleSubmit
, just before the call to searchUnsplash()
:
observer.observe(target);
There you go! We now have a fully functional Unsplash Search application, and it didn’t take a lot of work to build it out thanks to the help of Svelte and the Intersection Observer API.
The complete code for this tutorial can be found in this GitHub repository. If you’re interested in learning more about Svelte, consider subscribing to my email newsletter so you do not miss future tutorials.
Thanks for reading!
Comments
Ground rules
Please keep your comments relevant to the topic, and respectful. I reserve the right to delete any comments that violate this rule. Feel free to request clarification, ask questions or submit feedback.