Code Block

A code block highlighted with Shiki that can type its code out, character by character.

<script setup lang="ts">
import CodeBlock from '@/components/animate-ui/components/code-block/CodeBlock.vue'
import { useDark } from '@/lib/use-dark'

withDefaults(
  defineProps<{
    delay?: number | string
    duration?: number | string
    writing?: boolean | string
    cursor?: boolean | string
  }>(),
  { delay: 0, duration: 10000, writing: true, cursor: true },
)

const CODE = `'use client';

import * as React from 'react';

type MyComponentProps = {
  myProps: string;
} & React.ComponentProps<'div'>;

function MyComponent(props: MyComponentProps) {
  return (
    <div {...props}>
      <p>My Component</p>
    </div>
  );
}

export { MyComponent, type MyComponentProps };`

const on = (value: boolean | string) => value === true || value === 'true'
const dark = useDark()
</script>

<template>
  <CodeBlock
    :key="`${delay}-${duration}-${writing}`"
    :code="CODE"
    lang="tsx"
    :theme="dark ? 'dark' : 'light'"
    :writing="on(writing)"
    :delay="Number(delay)"
    :duration="Number(duration)"
    :cursor="on(cursor)"
    class="w-[420px] max-w-full"
    scroll-class="h-[372px] max-h-none"
  />
</template>

Installation

npm install @animate-ui/vue motion-v
pnpm add @animate-ui/vue motion-v
yarn add @animate-ui/vue motion-v
bun add @animate-ui/vue motion-v

The code is highlighted with Shiki, a peer dependency of this primitive, loaded on demand:

npm install shiki
pnpm add shiki
yarn add shiki
bun add shiki

Usage

<script setup lang="ts">
import { CodeBlock } from '@animate-ui/vue/primitives/code-block';
</script>

<template>
  <CodeBlock
    code="console.log('Hello, world!');"
    lang="javascript"
    writing
    :duration="2000"
  />
</template>

The block renders Shiki's <pre> inside a div. Style it through that div ([&_pre]:…, [&_.line]:…); data-writing and data-done let you show a cursor while it types.

Ready-made block

A styled block around the primitive, with a cursor while it types and a copy button, is a file you add to your project and own:

npx shadcn-vue@latest add https://animate-ui.com/r/vue/components-code-block.json
pnpm dlx shadcn-vue@latest add https://animate-ui.com/r/vue/components-code-block.json
yarn dlx shadcn-vue@latest add https://animate-ui.com/r/vue/components-code-block.json
bun x shadcn-vue@latest add https://animate-ui.com/r/vue/components-code-block.json
<script setup lang="ts">
import CodeBlock from '@/components/animate-ui/components/code-block/CodeBlock.vue';
</script>

<template>
  <CodeBlock
    code="console.log('Hello, world!');"
    lang="javascript"
    writing
    :duration="2000"
  />
</template>

It takes every prop of the primitive, plus:

PropTypeDefault
cursor?
booleantrue
copy-button?
booleantrue
scroll-class?
string-

Animation

  • With writing, the code appears one character at a time, evenly over duration (after delay), and the block and scroll-container (its parent element by default) scroll smoothly to follow the last line.
  • With in-view, writing starts when the block scrolls into view.
  • Under reduced motion, the whole code is shown at once (done is still emitted).

API Reference

CodeBlock

PropTypeDefault
code
string-
lang
string-
theme?
"light" | "dark""light"
themes?
{ light: string; dark: string }{ light: "github-light", dark: "github-dark" }
writing?
booleanfalse
duration?
number5000
delay?
number0
scroll-container?
HTMLElement | null-
in-view?
booleanfalse
in-view-margin?
string"0px"
in-view-once?
booleantrue
...props?
HTMLAttributes-

It emits done once the whole code is shown, and write ({ index, length, done }) on each written character, and once done.

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/23/2026