@markup-carve/webpack-loader

August 21, 2026 ยท View on GitHub

A webpack 5 loader for importing Carve markup as build-time-rendered HTML. It is also the supported bridge for Next.js projects that use webpack.

Install

npm install @markup-carve/webpack-loader

Webpack

export default {
  module: {
    rules: [
      { test: /\.crv$/, use: ['@markup-carve/webpack-loader'] },
    ],
  },
}
import html, { source, frontmatter } from './document.crv'

The default export and named html export contain rendered HTML. source contains the original document and frontmatter contains Carve's raw {format, content} metadata object (or null). Rendering happens during the build; the Carve engine is not sent to the browser.

Options are passed under carveOptions:

{ test: /\.crv$/, use: [{
  loader: '@markup-carve/webpack-loader',
  options: { carveOptions: { allowRawHtml: false } },
}] }

Next.js

Add the same rule in next.config.mjs:

export default {
  webpack(config) {
    config.module.rules.push({
      test: /\.crv$/,
      use: ['@markup-carve/webpack-loader'],
    })
    return config
  },
}

Then render an import from a Server Component:

import html from './welcome.crv'

export default function Page() {
  return <article dangerouslySetInnerHTML={{__html: html}} />
}

Next.js 16 uses Turbopack by default. Run next dev --webpack and next build --webpack with this configuration. Turbopack accepts a subset of webpack loaders, but this package only claims the webpack path until its ESM dependency loading has been verified in Turbopack itself.

Because rendering produces HTML, use Carve's allowRawHtml: false option when authors are not trusted. React's dangerouslySetInnerHTML does not sanitize.

TypeScript

declare module '*.crv' {
  export const source: string
  export const html: string
  export const frontmatter: {format: string; content: string} | null
  const value: string
  export default value
}

License

MIT