Code Block
Vue data-displayRead-only code preview with a header, optional filename, copy button, and `<pre>`-rendered content. Use for installation snippets, API examples, and snippets you want users to copy verbatim.
Also available for React ->Installation
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/code-block.json$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/code-block.json$ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/code-block.json$ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/code-block.json
Or with the named registry:
npx shadcn-vue@latest add @uipkge/code-block
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
code Source code to display. | string | — | required |
language Language label shown in the header. | string | 'vue' | optional |
showLineNumbers Render line numbers in the gutter. | boolean | true | optional |
maxHeight Maximum height of the code body before scrolling kicks in. | string | '400px' | optional |
defaultExpanded Render expanded on first paint. Default true. | boolean | true | optional |
showHeader Show the language label / copy / collapse header. | boolean | true | optional |
class | HTMLAttributes['class'] | — | optional |
Dependencies
Files (2)
-
app/components/ui/code-block/CodeBlock.vue 3.5 kB
<script setup lang="ts"> import { computed, ref } from 'vue' import { Check, ChevronDown, ChevronUp, Copy } from 'lucide-vue-next' import type { HTMLAttributes } from 'vue' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' interface Props { /** Source code to display. */ code: string /** Language label shown in the header. */ language?: string /** Render line numbers in the gutter. */ showLineNumbers?: boolean /** Maximum height of the code body before scrolling kicks in. */ maxHeight?: string /** Render expanded on first paint. Default true. */ defaultExpanded?: boolean /** Show the language label / copy / collapse header. */ showHeader?: boolean class?: HTMLAttributes['class'] } const props = withDefaults(defineProps<Props>(), { language: 'vue', showLineNumbers: true, maxHeight: '400px', defaultExpanded: true, showHeader: true, }) const isExpanded = ref(props.defaultExpanded) const copied = ref(false) const lines = computed(() => props.code.replace(/\s+$/, '').split('\n')) async function copyToClipboard() { try { await navigator.clipboard.writeText(props.code) copied.value = true setTimeout(() => (copied.value = false), 1600) } catch (e) { console.warn('Clipboard write failed', e) } } </script> <template> <div data-uipkge data-slot="code-block" :class="cn('group border-border bg-muted/20 relative overflow-hidden rounded-lg border', props.class)" > <!-- Header --> <div v-if="showHeader" class="border-border bg-muted/40 flex items-center justify-between gap-2 border-b px-3 py-2"> <span class="text-muted-foreground text-xs font-medium tracking-wide uppercase"> {{ language }} </span> <div class="flex items-center gap-1"> <Button variant="ghost" size="xs" class="h-7 gap-1.5 px-2" @click="copyToClipboard"> <Check v-if="copied" class="text-success size-3" aria-hidden="true" /> <Copy v-else class="size-3" aria-hidden="true" /> <span class="text-xs">{{ copied ? 'Copied' : 'Copy' }}</span> </Button> <Button variant="ghost" size="xs" class="h-7 gap-1.5 px-2" @click="isExpanded = !isExpanded"> <ChevronUp v-if="isExpanded" class="size-3" aria-hidden="true" /> <ChevronDown v-else class="size-3" aria-hidden="true" /> <span class="text-xs">{{ isExpanded ? 'Hide' : 'Show' }} code</span> </Button> </div> </div> <!-- Code body. We render line-by-line so the gutter and the code stay in sync without measuring text height. The renderer is intentionally plain: code is displayed as-is and the consumer can layer their own syntax highlighter (shiki, prism, hljs) on top by replacing this file or by wrapping it in their own component. --> <div v-show="isExpanded" class="bg-background/40 overflow-auto font-mono text-sm leading-relaxed" :style="{ maxHeight: props.maxHeight }" > <pre class="m-0 flex"><div v-if="showLineNumbers" aria-hidden="true" class="text-muted-foreground/60 sticky left-0 select-none border-r border-border/60 bg-muted/30 px-3 py-3 text-right tabular-nums" ><div v-for="(_, i) in lines" :key="i">{{ i + 1 }}</div></div><code class="block flex-1 px-4 py-3" ><template v-for="(line, i) in lines" :key="i"><span>{{ line || ' ' }}</span><br v-if="i < lines.length - 1"></template></code></pre> </div> </div> </template> -
app/components/ui/code-block/index.ts 0.1 kB
export { default as CodeBlock } from './CodeBlock.vue'
Raw manifest: https://uipkge.dev/r/vue/code-block.json