Rizal Renaldi

DRAFT
← Back

Taming Nuxt Content v3

Customization I applied for Nuxt Content v3

dev

1. Can't find a way to fully customized Shiki highlighter

So I override ProsePre component with following code. I added styling to header for file name and language. I also added copy button. Shiki documentation mentioned it can be done with transformer, but I can't find a way to integrate it into Nuxt Content. I also added line number, and I also can't find a way to "natively" activate line number, so I just use plain CSS for this.

    
ProsePre.vue
vue
<template> <pre class="flex mt-10 pt-16 pb-3.5 not-prose text-sm bg-gray bg-gray-50 dark:bg-gray-dark-700/70 border border-gray-100 dark:border-gray-dark-600/75 rounded-xl relative overflow-scroll whitespace-pre-wraps" :class="$props.class" :data-lang="language" > <div class="absolute top-0 left-0 py-2.5 h-12 px-4 w-full border-b select-none text-xs text-gray-700 dark:text-gray-dark-400 dark:border-gray-dark-600/75 border-gray-100 flex items-center justify-between font-sans group"> <div class="flex items-center gap-2"> <div class="size-2 filedot bg-brand rounded-full"></div> <span class="block">{{ filename ? filename : 'Code' }}</span> </div> <div class="flex items-center relative gap-2"> <span class="block">{{ language }}</span> <button class="cursor-pointer flex items-center justify-center bg-gray-50 dark:bg-[#1C1F24] hover:text-brand hover:bg-gray-100 dark:hover:bg-gray-dark-900 text-gray-700 dark:text-gray-dark-400 size-8 rounded-full" @click="copycode" > <IconCopy v-if="!copied" stroke="1" size="w-5" /> <IconCheckmark v-else color="text-brand" stroke="1" size="w-5" /> </button> </div> </div> <slot /> </pre> </template> <script setup lang="ts"> import { useClipboard, useDebounceFn } from '@vueuse/core' const props = defineProps({ code: { type: String, default: '' }, language: { type: String, default: null }, filename: { type: String, default: null }, highlights: { type: Array as () => number[], default: () => [] }, meta: { type: String, default: null }, class: { type: String, default: null } }) const { text, copy, copied, isSupported } = useClipboard() function copycode() { copy(props.code) } </script> <style> .filedot { box-shadow: 0 0 6px 0 #016AFF; } pre code .line { display: block; padding: 2px 8px; border-left: 3px solid transparent; } .dark pre code span.highlight { background-color: #292f35; position: relative; } pre code span.highlight { border-left: 3px solid #016AFF; } code { counter-reset: step; counter-increment: step calc(var(--start, 1) - 1); width: 100%; } code .line::before { content: counter(step); counter-increment: step; width: 1rem; margin-right: 1.5rem; display: inline-block; text-align: right; color: rgba(115, 138, 148, .4); } .light pre code span.highlight { background-color: #e2e6eb; } </style>

The Blog of Rizal Renaldi

2025 © rizalrenaldi.com — Made with 🖖