N
Nuxt4d ago
Norbiros

Typing animation with markdown

Hi! I am trying to test my Nuxt skills in more complex scenario. I want to create typing animation - it sounds simple, but I want to support markdown and I want to do that without sending no markdown related libraries to the client! I managed to implement component like this:
<template>
<div ref="typeInput">
<slot class="hidden">
<h2>Hello <span style="color:red;">World</span></h2>
<p>This is <strong>animated</strong> typing!</p>
</slot>
</div>

<div ref="typeTarget" style="white-space: pre-wrap;" />
</template>

... typing logic ...
<template>
<div ref="typeInput">
<slot class="hidden">
<h2>Hello <span style="color:red;">World</span></h2>
<p>This is <strong>animated</strong> typing!</p>
</slot>
</div>

<div ref="typeTarget" style="white-space: pre-wrap;" />
</template>

... typing logic ...
And In parent i am trying to pass rendered markdown and refresh it:
<AnimatedText ref="animatedText">
<!-- This is a server component -->
<MarkdownContent :text="currentMessage.message" class="text-xl md:text-2xl font-semibold" />
</AnimatedText>
<AnimatedText ref="animatedText">
<!-- This is a server component -->
<MarkdownContent :text="currentMessage.message" class="text-xl md:text-2xl font-semibold" />
</AnimatedText>
Problem is that I am using server components and I have to wait until it finishes rendering, sending to client and then run animation. But I have no idea how to do that, nextTick won't work... I really don't want to use markdown-it, because I managed to use mdc (from nuxt content) everywhere, so I don't want to add it only for this simple thing
4 Replies
kapa.ai
kapa.ai4d ago
Hi @Norbiros. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- I understand you're trying to create a typing animation that supports markdown content rendered on the server side. This is an interesting challenge that involves coordinating server-rendered content with client-side animations. The Challenge Your approach using a server component (MarkdownContent) inside your AnimatedText component creates a timing issue. The server component renders on the server, but your animation needs to run on the client after the content is fully available. Solution Options 1. Use onMounted with nextTick When using server components, you need to ensure your client-side code runs after the component is mounted and the DOM is updated. For .client components, you typically need to add await nextTick() in the onMounted() hook:
onMounted(async () => {
await nextTick()
// Your animation code here
})
onMounted(async () => {
await nextTick()
// Your animation code here
})
However, as you mentioned, this might not be sufficient when working with server components. 2. Use a Watcher for Content Changes You could watch for changes in the rendered content:
watch(() => props.content, async (newContent) => {
if (newContent) {
await nextTick()
// Start animation
}
}, { immediate: true })
watch(() => props.content, async (newContent) => {
if (newContent) {
await nextTick()
// Start animation
}
}, { immediate: true })
3. Use Nuxt Content's Binding Capabilities Since you're already using Nuxt Content, you could leverage its data binding capabilities to know when content is ready:
<template>
<div>
<ContentRenderer :value="data" @rendered="onContentRendered">
<!-- Your content here -->
</ContentRenderer>
</div>
</template>

<script setup>
const onContentRendered = () => {
// Start your animation here
}
</script>
<template>
<div>
<ContentRenderer :value="data" @rendered="onContentRendered">
<!-- Your content here -->
</ContentRenderer>
</div>
</template>

<script setup>
const onContentRendered = () => {
// Start your animation here
}
</script>
The ContentRenderer component from Nuxt Content can help you render markdown and know when it's done as shown in the documentation. 4. Server Component with Client Component Pairing Nuxt supports pairing server and client components. You could create:
components/
MarkdownTyping.server.vue
MarkdownTyping.client.vue
components/
MarkdownTyping.server.vue
MarkdownTyping.client.vue
The server component would render the markdown, and the client component would handle the animation as described in the documentation. Implementation Example Here's a potential approach using Nuxt Content's ContentRenderer:
<template>
<div>
<ContentRenderer v-if="markdownData" :value="markdownData" @rendered="startAnimation" />
<div ref="typeTarget" style="white-space: pre-wrap;" />
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const markdownData = ref(null)
const typeTarget = ref(null)

// Parse your markdown content
onMounted(async () => {
// Get your markdown content
markdownData.value = {
// Your markdown content structure
body: {
// Content structure that ContentRenderer expects
}
}
})

const startAnimation = () => {
// Your typing animation logic here
// This will run after the markdown is rendered
}
</script>
<template>
<div>
<ContentRenderer v-if="markdownData" :value="markdownData" @rendered="startAnimation" />
<div ref="typeTarget" style="white-space: pre-wrap;" />
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const markdownData = ref(null)
const typeTarget = ref(null)

// Parse your markdown content
onMounted(async () => {
// Get your markdown content
markdownData.value = {
// Your markdown content structure
body: {
// Content structure that ContentRenderer expects
}
}
})

const startAnimation = () => {
// Your typing animation logic here
// This will run after the markdown is rendered
}
</script>
If you want to avoid sending markdown libraries to the client, the server component approach is ideal. You can enable this with:
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
componentIslands: true
}
})
Then create a server component for markdown rendering as shown in this Stack Overflow example. Remember that server components must have a single root element and props are passed via URL parameters, so be careful with large content as noted in the documentation. Would you like me to elaborate on any specific part of this solution? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@770620808644919307> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Norbiros
NorbirosOP4d ago
I don't use @nuxt/content, only mdc. No options for waiting to server compoennt to refresh work @kapa.ai
kapa.ai
kapa.ai4d ago
Let me check the knowledge base for you again. One moment! :books: Sorry @Norbiros, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?

Did you find this page helpful?