How to create a progress indicator in a Svelte application
Why do we need a progress indicator?
Progress indicators in user interface design are often overlooked, but they have a significant impact on the user experience. They inform and engage users during tasks such as file downloads, content uploads, or process completion. Designing an effective and captivating progress indicator requires careful consideration.
In this Educative Answer, we will explore how to create a progress indicator in a Svelte application that specifically focuses on file uploads. As the upload progresses, we’ll display a progress indicator to show the user how far along they are in the upload process.
Note: We won’t go into the details of how to develop a Svelte application from scratch and are using the default template to display our progress indicator module. However, if you are interested in learning more about Svelte, you can find more information on the topic in our Building Reactive Apps with Svelte and Tailwind course.
The following screenshot shows what our application would look like:
Progress indicator implementation in Svelte
We will explore the implementation details of the progress bar in a Svelte application.
We’ll examine the HTML structure and the JavaScript logic that work together to create a dynamic and visually appealing progress indicator.
By understanding these components, we’ll be able to integrate a progress bar into our own Svelte applications and enhance the user experience during file uploads or other relevant tasks.
HTML structure
The HTML structure for the progress bar in the Svelte application consists of the following elements:
- Completion message container: This container, represented by the
<div id="msg-cont">element in our code, is responsible for displaying the completion message when the progress reaches . It wraps the completion message element<h3 id="completion-msg">.
<div id="msg-cont">{#if barWidth === 100}<h3 id="completion-msg" in:fly={{x: -300}} out:fly={{x: 300}}>The file has been uploaded!</h3>{/if}</div>
- Progress bar container: The
<div id="progress">element acts as the container for the progress bar and percentage display. It controls the visibility of these elements based on theisActivevariable.
<div id="progress">{#if isActive}<span>{barWidth}%</span><div id="myBar" style="width: {barWidth}%"></div>{/if}</div>
- Upload form: The
<form>element encapsulates the file upload functionality. It includes an<input type="file">element for selecting the file, along with an “Upload File” button and a “Cancel” button. ThemakeProgressandresetBarfunctions handle the form submission and cancellation, respectively.
<form on:submit|preventDefault={makeProgress}><input type="file" id="myFile" name="filename" bind:this={chooseFileBtn}><button disabled={isActive}>Upload File</button><button on:click|stopPropagation|preventDefault={resetBar}>Cancel</button></form>
This HTML structure defines the layout and functionality of the progress bar in the Svelte application.
JavaScript structure
The JavaScript structure for the progress bar in the Svelte application consists of the following elements:
-
Variables: The following code defines several variables using the
letkeyword:-
barWidth: Tracks the current progress of the upload, initially set to0. -
progress: Stores the interval reference for updating the progress bar. -
isActive: Indicates whether the progress bar is active or not. -
chooseFileBtn: Stores a reference to the file input element using thebind:thisdirective.
-
let barWidth = 0;let progress; // for setIntervallet isActive = false;let chooseFileBtn; // use with bind:this and the input type="file"
-
Reactive statement: The code utilizes a reactive statement to observe changes in the
barWidthvariable. WhenbarWidthreaches , the reactive statement triggers two actions:-
stopProgress(): Calls thestopProgressfunction to clear the interval and reset the input value after a delay of 2500ms. -
resetBar(): Calls theresetBarfunction to reset the progress bar to its initial state.
-
$: if (barWidth === 100) {stopProgress();setTimeout(resetBar, 2500);}
-
Functions
-
stopProgress(): This function stops the progress by clearing the interval and resetting the input value. It is invoked when the upload is completed or canceled. -
resetBar(): This function resets the progress bar by settingbarWidthto0and callingstopProgress(). -
addColor(): This function increments thebarWidthvariable by1, simulating progress. It is called repeatedly usingsetIntervalduring active progress. -
randomTime(): This function generates a random time value between0and60, used as the interval duration for updating the progress bar. -
makeProgress(): This function is triggered when the upload is initiated. It first checks if a file has been chosen usingchooseFileBtn.value. If a file is chosen, it setsisActivetotrueand callssetIntervalwithaddColorandrandomTimeto start updating the progress bar.
-
const stopProgress = () => {clearInterval(progress);isActive = false;chooseFileBtn.value = "";}const resetBar = () => {stopProgress();barWidth = 0;}const addColor = () => barWidth += 1;const randomTime = () => Math.floor(Math.random() * 60)const makeProgress = () => {if (!chooseFileBtn.value) {alert("Choose a file")} else {isActive = true;progress = setInterval(addColor, randomTime());}}
It provides the necessary functions to start, stop, and reset the progress and simulate progress updates using intervals.
The Svelte application
We can run our application by pressing the “Run” button in the widget below:
Note: There is some supplementary code included alongside the HTML and JS that we previously discussed. However, it is important to note that the code responsible for creating the progress bar remains largely unchanged, just as we discussed.
<script>
import { fade, fly } from 'svelte/transition';
let barWidth = 0;
let progress; // for setInterval
let isActive = false;
let isLoading = true; // added variable
let chooseFileBtn; // use with bind:this and the input type="file"
let showCompletionMsg = false; // added variable
let completionMsgVisible = false;
const stopProgress = () => {
clearInterval(progress);
isActive = false;
chooseFileBtn.value = "";
};
const resetBar = () => {
stopProgress();
barWidth = 0;
completionMsgVisible = false;
};
const addColor = () => {
if (barWidth < 100) {
barWidth += 1;
} else {
stopProgress();
showCompletionMsg = true;
setTimeout(() => {
showCompletionMsg = false;
resetBar();
}, 3000);
}
};
const randomTime = () => Math.floor(Math.random() * 60);
const makeProgress = () => {
if (isLoading) return;
if (!chooseFileBtn.value) {
alert("Choose a file");
} else {
isActive = true;
progress = setInterval(addColor, randomTime());
}
};
setTimeout(() => {
isLoading = false;
}, 10);
</script>
<style>
#progress {
width: 100%;
height: 50px;
position: relative;
margin-top: 5%;
display: flex;
align-items: center;
}
#myBar {
height: 40px;
background-color: #9ee86a;
border: 1px solid #333;
transition: width 0.3s;
position: relative;
overflow: hidden;
}
#myBar span {
font-size: 1.2rem;
color: white;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
form {
margin-top: 1%;
width: 100%;
text-align: center;
}
button {
cursor: pointer;
width: 100px;
padding: 9.5px 0;
}
button:active {
background-color: hsl(20, 58%, 55%);
color: white;
}
@keyframes blink {
to {
opacity: 0;
}
}
#loading-text {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#msg-cont {
height: 50px;
padding: 10px;
}
h3#completion-msg {
margin: 0 auto;
text-align: center;
animation: blink 1.5s ease-in-out 4;
}
</style>
{#if isLoading}
<!-- Show loading text while isLoading is true -->
<div id="loading-text">Loading...</div>
{:else}
<!-- Show the application content when isLoading is false -->
<div>
<div id="msg-cont">
{#if showCompletionMsg}
<h3 id="completion-msg" in:fly={{ x: -300 }} out:fly={{ x: 300 }}>
The file has been uploaded! The progress indicator works!
</h3>
{/if}
</div>
<div id="progress">
{#if isActive}
<div id="myBar" style="width: {barWidth}%">
<span>{barWidth}%</span>
</div>
{/if}
</div>
<form on:submit|preventDefault={makeProgress}>
<input type="file" id="myFile" name="filename" bind:this={chooseFileBtn}>
<button disabled={isActive}>Upload File</button>
<button on:click|stopPropagation|preventDefault={resetBar}>Cancel</button>
</form>
</div>
{/if}
Free Resources