Velite

Set up CodeGloss with Velite — a content build tool that compiles markdown/MDX into type-safe data collections.

This documentation site itself uses Velite + Next.js.

Install

npm install codegloss @codegloss/react velite

Configure Velite

Add remark-codegloss to your velite.config.ts. Use skipImport: true because the React wrapper is provided via the components map at render time. If you've declared a highlight in codegloss.config.ts (see Syntax Highlighters), thread it through so blocks are pre-highlighted at build:

import { defineConfig, s } from 'velite';
import remarkCodegloss from 'codegloss/remark';
import codeglossConfig from './codegloss.config';

export default defineConfig({
  mdx: {
    remarkPlugins: [
      [
        remarkCodegloss,
        { skipImport: true, highlight: codeglossConfig.highlight },
      ],
    ],
  },
  collections: {
    posts: {
      name: 'Post',
      pattern: 'posts/**/*.mdx',
      schema: s.object({
        title: s.string(),
        slug: s.path(),
        body: s.mdx(),
      }),
    },
  },
});

Render compiled MDX

Velite compiles MDX content into a JS function body string. Pass CodeGloss via the components map when you render it:

'use client';

import { useMemo } from 'react';
import * as runtime from 'react/jsx-runtime';
import { CodeGloss } from '@codegloss/react';

const components = { CodeGloss };

function useMdxComponent(code: string) {
  return useMemo(() => {
    const fn = new Function(code);
    return fn({ ...runtime }).default;
  }, [code]);
}

export function PostContent({ code }: { code: string }) {
  const Component = useMdxComponent(code);
  return <Component components={components} />;
}

No CSS import needed

CodeGloss bundles its styles inside Shadow DOM. There's nothing for you to import in your global stylesheet — the element registers itself when @codegloss/react is loaded.

With Next.js

Configure the Velite webpack plugin in next.config.ts:

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  webpack: (config) => {
    config.plugins.push(new VeliteWebpackPlugin());
    return config;
  },
};

class VeliteWebpackPlugin {
  static started = false;

  apply(compiler) {
    compiler.hooks.beforeCompile.tapPromise('VeliteWebpackPlugin', async () => {
      if (VeliteWebpackPlugin.started) return;
      VeliteWebpackPlugin.started = true;
      const { build } = await import('velite');
      const dev = compiler.options.mode === 'development';
      await build({ watch: dev, clean: !dev });
    });
  }
}

export default nextConfig;

The remark plugin runs during Velite's build step, so remarkPlugins goes in velite.config.ts only — not in any Next.js MDX config.

Consistent chrome on every code block (optional)

By default, only fences tagged with codegloss get the codegloss chrome (toolbar, line numbers, copy button). Plain fenced code blocks render as regular <pre> elements. If you'd rather have every code block on your site use the same visual, install @codegloss/shiki and add rehypeCodeglossPre to your rehype pipeline — it runs after rehype-shiki and rewrites every <pre><code> into a <code-gloss> element with the highlighted HTML baked in.

npm install @codegloss/shiki
import { rehypeCodeglossPre } from '@codegloss/shiki';

export default defineConfig({
  mdx: {
    rehypePlugins: [
      [rehypeShikiFromHighlighter, shiki, { theme: 'github-dark' }],
      rehypeCodeglossPre,
    ],
    // ... remarkPlugins as before
  },
});

Now every <pre><code> on your site renders inside a <code-gloss> element — same toolbar, line numbers, and copy button as your annotated blocks, with Shiki's highlighted HTML baked in at build time.

@codegloss/shiki also re-exports createShikiHighlighter and ShikiLikeHighlighter from codegloss/highlighters/shiki, so Shiki users can import everything from one package.