@ifc-viewer/core
v0.2.5
Published
Production-ready BIM viewer for web applications. Load, inspect, and interact with IFC files in the browser with measurements, clipping, AI visualization, and more.
Maintainers
Readme
@ifc-viewer/core
Professional IFC viewer library built with React and Three.js
A production-ready, feature-rich BIM viewer that lets you load, inspect, and interact with Building Information Model (IFC) files directly in the browser. Built on top of Three.js and the That Open Company ecosystem with zero native installations required.
✨ Features
- 🏗️ Full IFC Support - Load and render IFC 2x3 and IFC4 files
- 📐 Measurement Tools - Volume, length, area, angle measurements
- 🗺️ Interactive Minimap - Real-time overhead navigation view
- ✂️ Clipping Planes - Section cuts with transform gizmos
- 🎨 AI Visualizer - Transform BIM views into photorealistic renders (powered by HuggingFace)
- 📸 Screenshots - Capture high-resolution images
- 🎯 Element Selection - Click to inspect IFC properties
- 🌳 Relations Tree - Navigate spatial structure (Sites → Buildings → Storeys)
- 🎮 Multiple Navigation Modes - Orbit, First-Person, Plan view
- 📱 Mobile Friendly - Touch-optimized controls
- 🎨 Customizable - Theme, appearance, and feature flags
- ⚡ High Performance - Optimized for large models with fragment streaming
📦 Installation
npm install @ifc-viewer/core🚀 Quick Start
Basic Usage
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
const viewer = createIFCViewer({
container: document.getElementById('viewer-container'),
onModelLoaded: (meta) => console.log('Model loaded!', meta),
onError: (error) => console.error('Error:', error)
});HTML Setup
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#viewer-container {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="viewer-container"></div>
<script type="module">
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
const viewer = createIFCViewer({
container: document.getElementById('viewer-container')
});
</script>
</body>
</html>🎨 Configuration
Full Configuration Example
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
const viewer = createIFCViewer({
// Required: Container element
container: document.getElementById('viewer-container'),
// Optional: Customize appearance
appearance: {
world: {
backgroundColor: '#050816',
directionalLightColor: '#f1f5f9',
directionalLightPosition: [20, 35, 10],
},
grid: {
enabled: true,
color: '#10b981',
primarySize: 6,
secondarySize: 1,
distance: 250,
},
},
// Optional: Load a model on startup
initialModel: {
url: '/models/demo-project.ifc',
// OR use a File/Blob:
// file: myIfcFileBlob
},
// Optional: Enable/disable features
features: {
minimap: true,
measurement: true,
clipping: true,
floorplans: true,
aiVisualizer: false, // Requires HuggingFace API token
},
// Optional: Callbacks
onModelLoaded: (meta) => {
console.log('Model loaded:', meta);
},
onObjectSelected: (selection) => {
console.log('Selected objects:', selection);
},
onError: (error) => {
console.error('Viewer error:', error);
},
// Optional: Custom theme
theme: {
'--primary-color': '#667eea',
'--background-color': '#1a1a1a',
}
});🎯 API Reference
ViewerHandle
The createIFCViewer function returns a handle with the following methods:
loadModelFromUrl(url: string): Promise<void>
Load an IFC model from a URL:
await viewer.loadModelFromUrl('/path/to/model.ifc');loadModelFromFile(file: File): Promise<void>
Load an IFC model from a File object:
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
await viewer.loadModelFromFile(file);
});captureScreenshot(): Promise<string>
Capture a screenshot as a base64 data URL:
const screenshot = await viewer.captureScreenshot();
// Use as image src or download
const img = document.createElement('img');
img.src = screenshot;getCameraState(): Promise<CameraState>
Get current camera position and target:
const cameraState = await viewer.getCameraState();
console.log('Position:', cameraState.position);
console.log('Target:', cameraState.target);setCameraState(state: CameraState): Promise<void>
Set camera position and target:
await viewer.setCameraState({
position: { x: 10, y: 20, z: 30 },
target: { x: 0, y: 0, z: 0 }
});unmount(): void
Cleanup and unmount the viewer:
viewer.unmount();🎨 Embedding in Your Website
Full-Page Viewer
<style>
body { margin: 0; padding: 0; overflow: hidden; }
#viewer { width: 100vw; height: 100vh; }
</style>
<div id="viewer"></div>
<script type="module">
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
createIFCViewer({
container: document.getElementById('viewer')
});
</script>Embedded in Page Section
When embedding the viewer in a page section (not full-page), you need to override some default styles:
<style>
/* Wrapper with explicit height */
.viewer-wrapper {
width: 100%;
height: 600px;
position: relative;
overflow: hidden;
}
/* Override viewport units to container units */
#viewer-container .ifc-viewer-library-container.layout {
height: 100% !important;
width: 100% !important;
}
/* Contain sidebar to container */
#viewer-container .sidebar-slot {
position: absolute !important;
}
#viewer-container .sidebar-toggle {
position: absolute !important;
}
</style>
<div class="viewer-wrapper">
<div id="viewer-container"></div>
</div>
<script type="module">
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
createIFCViewer({
container: document.getElementById('viewer-container')
});
</script>Modal/Popup Viewer
<style>
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
z-index: 10000;
align-items: center;
justify-content: center;
padding: 40px;
}
.modal-overlay.active {
display: flex;
}
.modal-content {
width: 90vw;
height: 80vh;
max-width: 1400px;
max-height: 900px;
background: #1a1a1a;
border-radius: 12px;
position: relative;
overflow: hidden;
}
#modal-viewer {
width: 100%;
height: 100%;
position: relative;
}
/* Override for modal context */
#modal-viewer .ifc-viewer-library-container.layout {
height: 100% !important;
width: 100% !important;
}
#modal-viewer .sidebar-slot,
#modal-viewer .sidebar-toggle {
position: absolute !important;
}
</style>
<button onclick="openViewer()">Open Viewer</button>
<div class="modal-overlay" id="modal">
<button onclick="closeViewer()" style="position: absolute; top: 20px; right: 20px;">×</button>
<div class="modal-content">
<div id="modal-viewer"></div>
</div>
</div>
<script type="module">
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
let viewer = null;
window.openViewer = function() {
document.getElementById('modal').classList.add('active');
if (!viewer) {
viewer = createIFCViewer({
container: document.getElementById('modal-viewer')
});
}
};
window.closeViewer = function() {
document.getElementById('modal').classList.remove('active');
};
</script>🎯 Advanced Examples
Load Model with Configuration
const viewer = createIFCViewer({
container: document.getElementById('viewer'),
// Custom colors and lighting
appearance: {
world: {
backgroundColor: '#0b1120',
directionalLightColor: '#f8fafc',
directionalLightPosition: [25, 40, 10],
},
grid: {
color: '#22d3ee',
enabled: true,
primarySize: 8,
secondarySize: 1,
},
},
// Pre-load a model
initialModel: {
url: '/models/showroom.ifc',
},
// Minimal UI - disable some features
features: {
minimap: false,
aiVisualizer: false,
measurement: true,
clipping: true,
},
// Handle events
onModelLoaded: async (meta) => {
console.log('Model loaded with metadata:', meta);
// Capture initial view
const screenshot = await viewer.captureScreenshot();
console.log('Initial screenshot captured');
},
onObjectSelected: (selection) => {
// selection is a Map: fragmentID -> Set<expressID>
for (const [fragmentId, expressIds] of selection.entries()) {
console.log(`Fragment ${fragmentId}: Selected ${expressIds.size} elements`);
}
},
});React Integration
import { useEffect, useRef } from 'react';
import { createIFCViewer, ViewerHandle } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
function IFCViewerComponent() {
const containerRef = useRef<HTMLDivElement>(null);
const viewerRef = useRef<ViewerHandle | null>(null);
useEffect(() => {
if (!containerRef.current) return;
const viewer = createIFCViewer({
container: containerRef.current,
onModelLoaded: (meta) => {
console.log('Model loaded:', meta);
},
features: {
minimap: true,
measurement: true,
}
});
viewerRef.current = viewer;
return () => {
viewer.unmount();
};
}, []);
const handleLoadModel = async () => {
if (viewerRef.current) {
await viewerRef.current.loadModelFromUrl('/model.ifc');
}
};
return (
<div style={{ width: '100%', height: '100vh' }}>
<button onClick={handleLoadModel}>Load Model</button>
<div ref={containerRef} style={{ width: '100%', height: '100%' }} />
</div>
);
}File Upload Integration
<input type="file" id="file-input" accept=".ifc" />
<div id="viewer" style="width: 100vw; height: 100vh;"></div>
<script type="module">
import { createIFCViewer } from '@ifc-viewer/core';
import '@ifc-viewer/core/styles';
const viewer = createIFCViewer({
container: document.getElementById('viewer'),
onModelLoaded: (meta) => {
console.log('Model loaded successfully!');
}
});
document.getElementById('file-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file && file.name.endsWith('.ifc')) {
try {
await viewer.loadModelFromFile(file);
} catch (error) {
console.error('Failed to load model:', error);
}
}
});
</script>🤖 AI Visualizer Setup (Optional)
The AI Visualizer feature transforms your BIM views into photorealistic architectural renders using HuggingFace models.
- Get a free HuggingFace API token: https://huggingface.co/settings/tokens
- Create a
.envfile in your project:VITE_HUGGINGFACE_TOKEN=your_token_here - Enable the feature:
createIFCViewer({ container: document.getElementById('viewer'), features: { aiVisualizer: true } });
📋 Type Definitions
Full TypeScript support with exported types:
import type {
CreateViewerOptions,
ViewerHandle,
SelectionMap,
ViewerFeatureFlags,
ViewerAppearanceConfig,
InitialModelConfig,
} from '@ifc-viewer/core';🛠️ Browser Support
- ✅ Chrome/Edge 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Mobile browsers (iOS Safari, Chrome Android)
Requirements:
- WebGL 2.0
- ES2020 support
- WebAssembly support
📚 Common Issues
Issue: Viewer appears full-screen instead of contained
Solution: When embedding in a page section, apply these CSS overrides:
#your-container .ifc-viewer-library-container.layout {
height: 100% !important;
width: 100% !important;
}
#your-container .sidebar-slot,
#your-container .sidebar-toggle {
position: absolute !important;
}Issue: Sidebar extends beyond container
Solution: The viewer uses position: fixed by default for full-page mode. Override to absolute for embedded mode (see example above).
Issue: Model doesn't load
Troubleshooting:
- Check browser console for errors
- Verify IFC file is valid (not corrupted)
- Ensure file is accessible (CORS headers if loading from URL)
- Check file size - very large models (>100MB) may take time
Issue: WASM not found
Solution: Ensure web-ifc.wasm is accessible. If serving from a subdirectory, set:
import { Components } from '@thatopen/components';
Components.config.webIfcPath = '/path/to/web-ifc.wasm';🎬 Examples
Check out our live examples:
📖 Documentation
🤝 Contributing
Contributions are welcome! Please read our Contributing Guide.
📄 License
MIT © IFC Viewer Contributors
🙏 Acknowledgments
Built with:
- Three.js - 3D rendering engine
- That Open Company - BIM components ecosystem
- web-ifc - IFC parser
Questions? Open an issue or start a discussion
Need help integrating? Check out our detailed integration guide with complete examples.
