dxf-render
v1.2.0
Published
DXF parser and Three.js renderer — parse and render AutoCAD DXF files in the browser. Framework-agnostic.
Maintainers
Readme
dxf-render
Framework-agnostic DXF parser and Three.js renderer. Use with React, Svelte, vanilla JS, or any framework.
Live Demo — upload your DXF and see the rendering quality.
Try it now on StackBlitz: Vanilla TS | React | Vue | Leaflet + DXF | DXF to PDF
For Vue 3 components, see the dxf-vuer package.
Why dxf-render?
- Most entities — 21 rendered types including all dimension variants, LEADER, MULTILEADER, MLINE
- Variable-width polylines — per-vertex tapering, arrows, donuts rendered as mesh with miter joins
- Accurate rendering — linetype patterns, OCS transforms, hatch patterns, proper color resolution
- Two entry points — full renderer or parser-only (zero deps, works in Node.js)
- Battle-tested — 841 tests covering parser, renderer, and utilities
- Modern stack — TypeScript native, ES modules, tree-shakeable, Vite-built
- Framework-agnostic — works with React, Svelte, Angular, vanilla JS, or any framework
Installation
# Full renderer (parser + Three.js rendering)
npm install dxf-render three
# Parser only (no Three.js needed)
npm install dxf-renderQuick Start
Parse and render
import {
parseDxf,
createThreeObjectsFromDXF,
loadDefaultFont,
useCamera,
useControls,
} from "dxf-render";
import * as THREE from "three";
// Parse DXF text
const dxf = parseDxf(dxfText);
// Load embedded font for text rendering
await loadDefaultFont();
// Create Three.js objects
const { group, materials } = await createThreeObjectsFromDXF(dxf);
// Set up scene
const scene = new THREE.Scene();
scene.add(group);
const frustumSize = 100;
const aspect = myCanvas.clientWidth / myCanvas.clientHeight;
const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2,
(frustumSize * aspect) / 2,
frustumSize / 2,
frustumSize / -2,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer({ canvas: myCanvas });
renderer.setSize(myCanvas.clientWidth, myCanvas.clientHeight);
const { fitCameraToBox } = useCamera();
const { initControls } = useControls();
initControls(camera, myCanvas);
fitCameraToBox(new THREE.Box3().setFromObject(group), camera);
renderer.render(scene, camera);Parser only
import { parseDxf } from "dxf-render/parser";
import type { DxfData } from "dxf-render/parser";
import { isLineEntity } from "dxf-render/parser";
const dxf: DxfData = parseDxf(dxfText);
for (const entity of dxf.entities) {
if (isLineEntity(entity)) {
console.log(entity.startPoint, entity.endPoint);
}
}Async parsing (Web Worker)
import { parseDxfAsync, terminateParserWorker } from "dxf-render";
// Parses in a Web Worker, falls back to sync if Workers unavailable
const dxf = await parseDxfAsync(dxfText);
// Cleanup when done
terminateParserWorker();React example
import { useEffect, useRef } from "react";
import * as THREE from "three";
import {
parseDxf,
createThreeObjectsFromDXF,
loadDefaultFont,
useCamera,
useControls,
} from "dxf-render";
export function DxfViewer({ dxfText }: { dxfText: string }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current!;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const aspect = width / height;
const frustumSize = 100;
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(width, height);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2,
(frustumSize * aspect) / 2,
frustumSize / 2,
frustumSize / -2,
0.1,
1000,
);
const { fitCameraToBox } = useCamera();
const { initControls } = useControls();
let disposed = false;
(async () => {
await loadDefaultFont();
const dxf = parseDxf(dxfText);
const { group } = await createThreeObjectsFromDXF(dxf);
if (disposed) return;
scene.add(group);
initControls(camera, canvas);
const box = new THREE.Box3().setFromObject(group);
fitCameraToBox(box, camera);
renderer.render(scene, camera);
})();
return () => {
disposed = true;
renderer.dispose();
};
}, [dxfText]);
return <canvas ref={canvasRef} style={{ width: "100%", height: "500px" }} />;
}Svelte example
<script>
import { onMount, onDestroy } from "svelte";
import * as THREE from "three";
import {
parseDxf,
createThreeObjectsFromDXF,
loadDefaultFont,
useCamera,
useControls,
} from "dxf-render";
export let dxfText;
let canvas;
let renderer;
onMount(async () => {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const aspect = width / height;
const frustumSize = 100;
renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(width, height);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2, (frustumSize * aspect) / 2,
frustumSize / 2, frustumSize / -2, 0.1, 1000,
);
const { fitCameraToBox } = useCamera();
const { initControls } = useControls();
await loadDefaultFont();
const dxf = parseDxf(dxfText);
const { group } = await createThreeObjectsFromDXF(dxf);
scene.add(group);
initControls(camera, canvas);
const box = new THREE.Box3().setFromObject(group);
fitCameraToBox(box, camera);
renderer.render(scene, camera);
});
onDestroy(() => renderer?.dispose());
</script>
<canvas bind:this={canvas} style="width: 100%; height: 500px;" />API
Entry points
| Import | Description |
| ------------------- | --------------------------------------------------- |
| dxf-render | Full API: parser + renderer + scene helpers + utils |
| dxf-render/parser | Parser only, zero dependencies |
Parser
parseDxf(text: string): DxfData— synchronous DXF parserparseDxfAsync(text: string): Promise<DxfData>— async parser via Web WorkerterminateParserWorker(): void— terminate the parser Web Worker
Renderer
createThreeObjectsFromDXF(dxf, options?): Promise<CreateDXFSceneResult>— create Three.js objects from parsed DXF dataoptions.signal—AbortSignalfor cancellationoptions.onProgress— progress callback (0–1)options.darkTheme— dark theme modeoptions.font— custom opentype.js Font object
MaterialCacheStore— material cache withswitchTheme()for instant dark mode
Scene helpers
useCamera(domElement)— orthographic camera withfitCameraToBox()useControls(camera, domElement)— pan/zoom controls (no rotation), mobile touch support
Fonts
loadDefaultFont(): Promise<Font>— load embedded Liberation Sans RegularloadFont(url: string): Promise<Font>— load custom .ttf/.otf fontgetDefaultFont(): Font | null— get loaded default font
Utils
resolveEntityColor()— resolve entity color with full priority chainresolveEntityLinetype()— resolve entity linetypecollectDXFStatistics()— collect file statisticsgetInsUnitsScale()— unit conversion factor
Types
Full TypeScript types exported: DxfData, DxfEntity, DxfLayer, DxfHeader, and 25+ entity-specific types with type guards (isLineEntity, isCircleEntity, etc.).
Supported entities
21 rendered entity types: LINE, CIRCLE, ARC, ELLIPSE, POINT, POLYLINE, LWPOLYLINE, SPLINE, TEXT, MTEXT, DIMENSION, INSERT, SOLID, 3DFACE, HATCH, LEADER, MULTILEADER, MLINE, XLINE, RAY, ATTDEF, plus ATTRIB within INSERT blocks and HELIX via SPLINE.
POLYLINE/LWPOLYLINE support includes per-vertex variable width (tapering), constant-width segments, arrows, donuts, and bulge arcs — all rendered as triangle-strip mesh geometry with proper miter joins at corners.
Comparison
| Feature | dxf-render | dxf-viewer | dxf-parser | three-dxf | | ------------------------- | ------------------------- | ------------ | ---------- | --------- | | DXF parsing | ✅ | ✅ | ✅ | ✅ | | Three.js rendering | ✅ | ✅ | ❌ | ✅ | | Entity types | 21 rendered | ~15 | ~15 parsed | ~8 | | Variable-width polylines | ✅ tapering, arrows, donuts| ❌ | — | ❌ | | Linetype patterns | ✅ DASHED, CENTER, DOT... | ❌ all solid | — | ❌ | | All dimension types | ✅ 7 types | linear only | — | ❌ | | LEADER / MULTILEADER | ✅ | ❌ | — | ❌ | | HATCH patterns | ✅ 25 built-in | ✅ | — | ❌ | | OCS (Arbitrary Axis) | ✅ full | Z-flip only | — | ❌ | | Vector text (opentype.js) | ✅ | ✅ | — | ❌ | | Geometry merging | ✅ | ✅ | — | ❌ | | Dark theme | ✅ instant switch | bg only | — | ❌ | | TypeScript | ✅ native | .d.ts | ✅ | ❌ | | Tests | 854 tests | 0 | ✅ | 0 | | Web Worker parsing | ✅ | ✅ | ❌ | ❌ | | Parser-only entry | ✅ zero deps | ❌ | ✅ | ❌ | | Framework | agnostic | agnostic | — | agnostic | | Bundle size | ~960KB | ~1.2MB | ~50KB | ~30KB | | Last updated | 2026 | 2024 | 2023 | 2019 |
Bundle sizes
| File | Size | Note | | ------------ | ------- | -------------------------------------------- | | Main bundle | ~960 KB | Includes font + opentype.js + inline worker | | Parser chunk | ~50 KB | Zero dependencies | | Serif font | ~525 KB | Lazy-loaded only when serif fonts referenced |
License
MIT
