1 krRBi 1Trh7vGl9xRvjEww
10 Min Read
Published: 4/28/2025

Create a svelte slider from scratch without any external tools

For too long I have relied on third party plugins and tools for simple sliders. Until one fine day a light bulb switched on in my tiny head that I can make my own in about 10 minutes that will be lightweight and sufficient for my basic slider needs.

We start by installing sveltekit using the following command

npm create svelte@latest svelte-slider

Then we cd into the directory (Note: You can add a dot instead of folder name to install in the same directory).

Choose a skeleton project and simple javascript or typescript is sufficient for our project.

npm create svelte@latest .
npm i
npm run dev -- --open

This is pretty much it.

Now open routes and +page.svelte remove everything.

Lets create a component called slider.svelte and export two props “duration” and “slides”.

<script>
export let duration;
export let slides;
let currentSlide = 0;
</script>

The variable currentSlide is used to track the state of the slider.

Next we import the slider component into page.svelte

<script>
import Slider from "../lib/slider.svelte";
import "../lib/style.css";
</script>
<Slider
duration={2000}
slides={[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]}
/>

For the purpose of our demonstration we are using a simple array as slides but you can use pretty much anything, string, images or a collection of both. Duration is set to 2000ms so our slider will change slides every 2 seconds.

Next up we set the markup for html in the slider component.

The way we are doing this is using the if condition to check if the currentSlide is equal to the slide index from the each iterator and only show that slide.

<div class="slider">
  {#each slides as slider, i}
    {#if currentSlide === i}
      <div class="slide">
{slider}
      </div>
    {/if}
  {/each}
</div>

Next up we need a function to increment the currentSlide. Lets call this function something very creative and out of the box like “nextSlide”. xD

So basically we take the currentSlide and increment it by one currentSlide++.

But it will keep on incrementing forever and we can use an if statement to prevent that and when the currentSlide is greater or equal to the number of the last slide we reset it to 0.

    <script>
      export let duration;
      export let slides;
      let currentSlide = 0;
      const nextSlide = () => {
currentSlide++;
if (currentSlide >= slides.length) {
  currentSlide = 0;
}
timer();
      };

      let interval;
      const timer = () => {
clearInterval(interval);
interval = setInterval(nextSlide, duration);
      };
      timer();
    </script>

Also we need a setInterval function lets call it timer. Basically we need to clearInterval otherwise very bad (mildly annoying) things will happen. Also we need to call the timer function in nextSlide function as well to reset the interval and not have stuttering while navigating the slider.

<div class="slider">
  {#each slides as slider, i}
    {#if currentSlide === i}
      <div class="slide">
        {slider}
      </div>
    {/if}
  {/each}
  <button class="next" on:click={nextSlide}>Next</button>
  <button class="prev" on:click={prevSlide}>Prev</button>
</div>

Lets add the buttons so we can also manually control the slider as well. We can use the on:click={} to call our function.

Also lets add a fancy navigation to spice things up.

<div class="slider">
  {#each slides as slider, i}
    {#if currentSlide === i}
      <div class="slide" transition:blur={{ amount: 10 }}>
        {slider}
      </div>
    {/if}
  {/each}
  <button class="next" on:click={nextSlide}>Next</button>
  <button class="prev" on:click={prevSlide}>Prev</button>
  <div class="nav">
    {#each slides as slider, i}
      <button
        class="bubble"
        on:click={() => {
          goToSlide(i);
        }}
        class:current={i === currentSlide}
        class:onedown={i === currentSlide - 1}
        class:twodown={i === currentSlide - 2}
        class:oneup={i === currentSlide + 1}
        class:twoup={i === currentSlide + 2}
      />
    {/each}
  </div>
</div>

This looks complex but its very simple. We have a simple nav that has a button corresponding to each slide and and on click to go to the clicked slide’s index.

A bunch of conditional classes so we can style the buttons. Now lets add a bunch of CSS styles to make everything look nice.

<style>
  .slider {
    width: 100vw;
    height: 100vh;
    position: relative;
    background-color: #222;
  }
  .slide {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 7rem;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
      Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
      sans-serif;
    font-weight: 200;
  }
  .next,
  .prev {
    position: absolute;
    z-index: 2;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: 1px solid #fff;
    color: #fff;
    padding: 4px 10px;
    border-radius: 4px;
    cursor: pointer;
  }
  .next {
    right: 20px;
  }
  .prev {
    left: 20px;
  }
  .nav {
    position: absolute;
    bottom: 20px;
    left: 0;
    right: 0;
    height: 100px;
    z-index: 3;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
  }
  .bubble {
    padding: 0;
    border: 0;
    height: 20px;
    width: 10px;
    border-radius: 100px;
    transition: all 0.3s ease-out;
    opacity: 0.2;
    cursor: pointer;
  }
  .current {
    height: 80px;
    opacity: 0.8;
  }
  .onedown,
  .oneup {
    height: 60px;
  }
  .twodown,
  .twoup {
    height: 40px;
  }
</style>

Also lets complete our script portion

<script>
  import { blur } from "svelte/transition";
  export let duration;
  export let slides;
  let currentSlide = 0;
  const nextSlide = () => {
    currentSlide++;
    if (currentSlide >= slides.length) {
      currentSlide = 0;
    }
    timer();
  };
  const prevSlide = () => {
    currentSlide--;
    if (currentSlide <= 0) {
      currentSlide = slides.length - 1;
    }
    timer();
  };
  const goToSlide = (i) => {
    currentSlide = i;
    timer();
  };
  let interval;
  const timer = () => {
    clearInterval(interval);
    interval = setInterval(nextSlide, duration);
  };
  timer();
</script>

Here is complete code and our slider is ready to be utilized in your upcoming projects.

<script>
  import { blur } from "svelte/transition";
  export let duration;
  export let slides;
  let currentSlide = 0;
  const nextSlide = () => {
    currentSlide++;
    if (currentSlide >= slides.length) {
      currentSlide = 0;
    }
    timer();
  };
  const prevSlide = () => {
    currentSlide--;
    if (currentSlide <= 0) {
      currentSlide = slides.length - 1;
    }
    timer();
  };
  const goToSlide = (i) => {
    currentSlide = i;
    timer();
  };
  let interval;
  const timer = () => {
    clearInterval(interval);
    interval = setInterval(nextSlide, duration);
  };
  timer();
</script>
<div class="slider">
  {#each slides as slider, i}
    {#if currentSlide === i}
      <div class="slide" transition:blur={{ amount: 10 }}>
        {slider}
      </div>
    {/if}
  {/each}
  <button class="next" on:click={nextSlide}>Next</button>
  <button class="prev" on:click={prevSlide}>Prev</button>
  <div class="nav">
    {#each slides as slider, i}
      <button
        class="bubble"
        on:click={() => {
          goToSlide(i);
        }}
        class:current={i === currentSlide}
        class:onedown={i === currentSlide - 1}
        class:twodown={i === currentSlide - 2}
        class:oneup={i === currentSlide + 1}
        class:twoup={i === currentSlide + 2}
      />
    {/each}
  </div>
</div>
<style>
  .slider {
    width: 100vw;
    height: 100vh;
    position: relative;
    background-color: #222;
  }
  .slide {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    color: #fff;
    font-size: 7rem;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
      Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
      sans-serif;
    font-weight: 200;
  }
  .next,
  .prev {
    position: absolute;
    z-index: 2;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: 1px solid #fff;
    color: #fff;
    padding: 4px 10px;
    border-radius: 4px;
    cursor: pointer;
  }
  .next {
    right: 20px;
  }
  .prev {
    left: 20px;
  }
  .nav {
    position: absolute;
    bottom: 20px;
    left: 0;
    right: 0;
    height: 100px;
    z-index: 3;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
  }
  .bubble {
    padding: 0;
    border: 0;
    height: 20px;
    width: 10px;
    border-radius: 100px;
    transition: all 0.3s ease-out;
    opacity: 0.2;
    cursor: pointer;
  }
  .current {
    height: 80px;
    opacity: 0.8;
  }
  .onedown,
  .oneup {
    height: 60px;
  }
  .twodown,
  .twoup {
    height: 40px;
  }
</style>

We also added a transition from svelte transitions to spice things up.

Here is the github repository. https://github.com/khalildevo/Svelte-Slider