npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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.

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.

npm version License: MIT

✨ 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.

  1. Get a free HuggingFace API token: https://huggingface.co/settings/tokens
  2. Create a .env file in your project:
    VITE_HUGGINGFACE_TOKEN=your_token_here
  3. 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:

  1. Check browser console for errors
  2. Verify IFC file is valid (not corrupted)
  3. Ensure file is accessible (CORS headers if loading from URL)
  4. 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:


Questions? Open an issue or start a discussion

Need help integrating? Check out our detailed integration guide with complete examples.