Migration Guide: Upgrading to Secondary Entry Points

June 28, 2026 · View on GitHub

Starting with version 0.21.0, ShipUI has transitioned from a single unified bundle import to a Modular Secondary Entry Points architecture. This guide walks you through why we made this change and how to update your codebase.

Note

Update (v0.23.0): The remaining core directives (ShipTooltip, ShipInputMask, ShipFileDragDrop, and ShipPreventWheel) have also been moved to their respective secondary entry points (e.g. @ship-ui/core/ship-tooltip). If you are using these directives in your application, you must update their import paths.


Why Secondary Entry Points?

Previously, all components were exported from the main @ship-ui/core entry point. While convenient, this had several drawbacks:

  1. Slower Dev Server (HMR): During development (ng serve), changing a single file forced the Vite dev server to compile and load all 35+ components and their styles, resulting in slower hot-reload times.
  2. Fragile Tree-Shaking: If a single component or utility in the main bundle contained a side-effect, the bundler was forced to include the entire library in your production bundles, causing bundle bloat.
  3. IDE Performance: Autocomplete and type checking were slower because the TypeScript language server had to parse the entire package at once.

Under the new model, each component is built and served as a separate, self-contained sub-package (e.g., @ship-ui/core/ship-button), yielding faster development startup/reload times, smaller production bundles, and a snappier IDE experience.


Step-by-Step Migration

1. Update TypeScript Component Imports

You must separate component imports from the main @ship-ui/core entry point and direct them to their respective subpaths.

❌ Before:

import { Component } from '@angular/core';
import { ShipButton, ShipIcon, ShipCard, ShipTooltip } from '@ship-ui/core';

@Component({
  selector: 'app-my-feature',
  standalone: true,
  imports: [ShipButton, ShipIcon, ShipCard, ShipTooltip],
  template: `...`
})
export class MyFeatureComponent {}

After:

import { Component } from '@angular/core';
// Components and directives import from their respective secondary entry points
import { ShipButton } from '@ship-ui/core/ship-button';
import { ShipIcon } from '@ship-ui/core/ship-icon';
import { ShipCard } from '@ship-ui/core/ship-card';
import { ShipTooltip } from '@ship-ui/core/ship-tooltip';

// Global utilities continue to import from the primary entry point
import { shipComponentClasses } from '@ship-ui/core';

@Component({
  selector: 'app-my-feature',
  standalone: true,
  imports: [ShipButton, ShipIcon, ShipCard, ShipTooltip],
  template: `...`
})
export class MyFeatureComponent {}

2. Verify Stylesheet Setup

The global stylesheet configuration remains backward compatible. Ensure your application's styles.scss continues to load the root variables, sheet utility, and basic resets:

@use '@ship-ui/core/styles';

If you configure shadow styles or font setups, make sure to pass them down:

@use '@ship-ui/core/styles' with (
  $useInterTight: true,
  $shipButtonShadow: true,
  $shipFormFieldShadow: true
);

3. Verify Asset Glob Mapping (angular.json)

Double check your angular.json configuration. Ensure the input directory resolves to the scoped package:

"assets": [
  "src/assets",
  {
    "glob": "**/*",
    "input": "./node_modules/@ship-ui/core/assets",
    "output": "./ship-ui-assets/"
  }
]