The Perfect Infinite Scroll Marquee Component for Nuxt
Michael Synan
Posted on November 26, 2023
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>
You'll notice the
baseFontSize
value is applied inside of theonMounted
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 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!
Posted on November 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.