DEV Community

Cover image for The Perfect Infinite Scroll Marquee Component for Nuxt
Michael Synan
Michael Synan

Posted on • Edited on

The Perfect Infinite Scroll Marquee Component for Nuxt

Creating a Scrolling Marquee Component in Nuxt


Have you ever needed a smooth, continuous scrolling marquee for your Nuxt projects? Inspired by the modern approach of Ryan Mulligan, I've created the perfect responsive Nuxt component for you. Ryan Mulligan's method serves as a great foundation for this implementation, so feel free to check it out for a more in-depth explanation of the CSS involved.

Key Features of the Scrolling Marquee Nuxt Component

  • Responsive Font Sizing: Ensuring that your marquee looks great on devices of all sizes.
  • Customizable Speed: Adjust the scrolling speed to match the pacing of your site.
  • Base Font and Message Props Options: Tailor the font style and the message content to fit your design needs.

This component is a practical addition to your Nuxt project—simply copy the entire component and make note of the available props. With the ability to easily modify the speed, font, and message, you can integrate this marquee into various parts of your web applications, whether it's for announcements, promotions, or just some dynamic flair.

Component Code

<template>
  <div v-if="isFontSizeSet" class="marquee">
    <div
      class="marquee__content"
      :style="{ animationDuration: animationSpeed }"
    >
      <span
        v-for="(letter, index) in (message + ' ').split('')"
        :key="index"
        class="marquee__item"
      >
        {{ letter }}
      </span>
    </div>
    <div
      class="marquee__content"
      :style="{ animationDuration: animationSpeed }"
    >
      <span
        v-for="(letter, index) in (message + ' ').split('')"
        :key="index + 1000"
        class="marquee__item"
      >
        {{ letter }}
      </span>
    </div>
  </div>
</template>
<script setup>
import { computed, watchEffect, onMounted } from 'vue';

const props = defineProps({
  fontSize: {
    type: String,
    default: '16px',
  },
  message: {
    type: String,
    default: 'THIS IS THE DEFAULT MESSAGE',
  },
  speed: {
    type: Number,
    default: 10,
  },
});

const animationSpeed = computed(() => `${props.speed}s`);
const isFontSizeSet = ref(false);

onMounted(() => {
  watchEffect(() => {
    document.documentElement.style.setProperty(
      '--baseFontSize',
      props.fontSize
    );
    isFontSizeSet.value = true;
  });
});
</script>

<style>
.marquee {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  overflow: hidden;
  user-select: none;
  display: flex;
  gap: var(--gap);
  --gap: 1rem;
}

.marquee__content {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-start;
  min-width: 100%;
  gap: var(--gap);
  animation: scroll linear infinite;
}

.marquee__item {
  white-space: nowrap;
  font-size: var(--baseFontSize);
  margin-right: 1px;
}

@media (max-width: 600px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 0.5);
  }
}

@media (min-width: 601px) and (max-width: 1024px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 0.75);
  }
}

@media (min-width: 1025px) {
  .marquee__item {
    font-size: calc(var(--baseFontSize) * 2);
  }
}

@keyframes scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(calc(-100% - var(--gap)));
  }
}
</style>
Enter fullscreen mode Exit fullscreen mode

You'll notice the baseFontSize value is applied inside of the onMounted lifecycle hook preventing rendering before the style is set.

Inside Parent

Then in the parent you can simply do something like this:

<ScrollingMarquee fontSize="100px" message="My Awesome Marquee" speed="6" />
Enter fullscreen mode Exit fullscreen mode

You can find a fully working example here: https://stackblitz.com/edit/nuxt-starter-emzrvl?file=app.vue

As someone who's spent a lot of time trying to get the perfect scrolling marquee code, I'm excited to share this component with the community.


I'm always happy to connect with fellow #vue or #nuxt developers. If you find this component useful or have any suggestions for improvement, feel free to reach out to me on LinkedIn or check out my personal website. Your feedback and connections are invaluable as we all strive to build a more vibrant and innovative web development community.


Remember to share your experiences and insights if you integrate this component into your projects. Happy coding!


Top comments (2)

Collapse
 
dijuansydnor profile image
Di'Juan Sydnor

First I want to thank you for this as this is something I can see me using on my project that I'm planning to cook.

I'm getting this error when trying to see the preview.
Failed to boot WebContainer

Booting WebContainer

Collapse
 
michaelsynan profile image
Michael Synan

Not sure why you're issue, but the StackBlitz still works fine on boot. Feel free to reach out on LinkedIn and I can help you out: linkedin.com/in/hellomichaelsynan/

Sorry for the late reply!