The Perfect Infinite Scroll Marquee Component for Nuxt
Tags: webdev, javascript, nuxt, vue
Published: 2023-11-26 18:17:19


## 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](https://ryanmulligan.dev/blog/css-marquee/) 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>
```

> 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" />
```
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](https://www.linkedin.com/in/hellomichaelsynan/) or check out my [personal website](https://www.michaelsynan.com). 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!

---