PageSourceSearch

https://stenciljs.com/docs/assets/js/a44622a8.7198c53d.js

js stenciljs.com collected 2026-10-02 03:21:34 UTC 14,252 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk_stencil_stencil_site=self.webpackChunk_stencil_stencil_site||[]).push([[4225],{92210:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>r,default:()=>h,frontMatter:()=>i,metadata:()=>a,toc:()=>d});var s=t(74848),o=t(28453);const i={title:"Publishing A Component Library",sidebar_label:"Publishing",description:"Publishing A Component Library",slug:"/publishing"},r=void 0,a={id:"guides/publishing",title:"Publishing A Component Library",description:"Publishing A Component Library",source:"@site/versioned_docs/version-v4.41/guides/publishing.md",sourceDirName:"guides",slug:"/publishing",permalink:"/docs/v4.41/publishing",draft:!1,unlisted:!1,editUrl:"https://github.com/ionic-team/stencil-site/tree/main/versioned_docs/version-v4.41/guides/publishing.md",tags:[],version:"v4.41",frontMatter:{title:"Publishing A Component Library",sidebar_label:"Publishing",description:"Publishing A Component Library",slug:"/publishing"},sidebar:"docs",previous:{title:"Bundling",permalink:"/docs/v4.41/module-bundling"},next:{title:"Server Side Rendering",permalink:"/docs/v4.41/server-side-rendering"}},c={},d=[{value:"Use Cases",id:"use-cases",level:2},{value:"Lazy Loading",id:"lazy-loading",level:3},{value:"Considerations",id:"considerations",level:4},{value:"Standalone",id:"standalone",level:3},{value:"Considerations",id:"considerations-1",level:4},{value:"Usage in TypeScript",id:"usage-in-typescript",level:4},{value:"Publishing to NPM",id:"publishing-to-npm",level:2}];function l(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",h4:"h4",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(n.p,{children:["There are numerous strategies to publish and distribute your component library to be consumed by external projects. One of the benefits of Stencil is that is makes it easy to generate the various ",(0,s.jsx)(n.a,{href:"/docs/v4.41/output-targets",children:"output targets"})," that are right for your use-case."]}),"\n",(0,s.jsx)(n.h2,{id:"use-cases",children:"Use Cases"}),"\n",(0,s.jsxs)(n.p,{children:["To use your Stencil components in other projects, there are two different output targets to consider: ",(0,s.jsx)(n.a,{href:"/docs/v4.41/distribution",children:(0,s.jsx)(n.code,{children:"dist"})})," and ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements",children:(0,s.jsx)(n.code,{children:"dist-custom-elements"})}),". Both export your components for different use cases. Luckily, both can be generated at the same time, using the same source code, and shipped in the same distribution. It would be up to the consumer of your component library to decide which build to use."]}),"\n",(0,s.jsx)(n.h3,{id:"lazy-loading",children:"Lazy Loading"}),"\n",(0,s.jsxs)(n.p,{children:["If you prefer to have your components automatically loaded when used in your application, we recommend enabling the ",(0,s.jsx)(n.a,{href:"/docs/v4.41/distribution",children:(0,s.jsx)(n.code,{children:"dist"})})," output target. The bundle gives you a small entry file that registers all your components and defers loading the full component logic until it is rendered in your application. It doesn't matter if the actual application is written in HTML or created with vanilla JavaScript, jQuery, React, etc."]}),"\n",(0,s.jsxs)(n.p,{children:["Your users can import your component library, e.g. called ",(0,s.jsx)(n.code,{children:"my-design-system"}),", either via a ",(0,s.jsx)(n.code,{children:"script"})," tag:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-html",children:'<script type="module" src="https://unpkg.com/my-design-system"><\/script>\n'})}),"\n",(0,s.jsx)(n.p,{children:"or by importing it in the bootstrap script of your application:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import 'my-design-system';\n"})}),"\n",(0,s.jsxs)(n.p,{children:["To ensure that the right entry file is loaded when importing the project, define the following fields in your ",(0,s.jsx)(n.code,{children:"package.json"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n  "exp
1orts": "./dist/esm/my-design-system.js",\n  "main": "./dist/cjs/my-design-system.js",\n  "unpkg": "dist/my-design-system/my-design-system.esm.js",\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["Read more about various options when it comes to configuring your project's components for lazy loading in the ",(0,s.jsx)(n.a,{href:"/docs/v4.41/distribution",children:(0,s.jsx)(n.code,{children:"dist"})})," output target section."]}),"\n",(0,s.jsx)(n.h4,{id:"considerations",children:"Considerations"}),"\n",(0,s.jsxs)(n.p,{children:["To start, Stencil was designed to lazy-load itself only when the component was actually used on a page. There are many benefits to this approach, such as simply adding a script tag to any page and the entire library is available for use, yet only the components actually used are downloaded. For example, ",(0,s.jsx)(n.a,{href:"https://www.npmjs.com/package/@ionic/core",children:(0,s.jsx)(n.code,{children:"@ionic/core"})})," comes with over 100 components, but a webpage may only need ",(0,s.jsx)(n.code,{children:"ion-toggle"}),". Instead of requesting the entire component library, or generating a custom bundle for just ",(0,s.jsx)(n.code,{children:"ion-toggle"}),", the ",(0,s.jsx)(n.code,{children:"dist"})," output target is able to generate a tiny entry build ready to load any of its components on-demand."]}),"\n",(0,s.jsxs)(n.p,{children:["However be aware that this approach is not ideal in all cases. It requires your application to ship the bundled components as static assets in order for them to load properly. Furthermore, having many nested component dependencies can have an impact on the performance of your application. For example, given you have a component ",(0,s.jsx)(n.code,{children:"CmpA"})," which uses a Stencil component ",(0,s.jsx)(n.code,{children:"CmpB"})," which itself uses another Stencil component ",(0,s.jsx)(n.code,{children:"CmpC"}),". In order to fully render ",(0,s.jsx)(n.code,{children:"CmpA"})," the browser has to load 3 scripts sequentially which can result in undesired rendering delays."]}),"\n",(0,s.jsx)(n.h3,{id:"standalone",children:"Standalone"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements",children:(0,s.jsx)(n.code,{children:"dist-custom-elements"})})," output target builds each component as a stand-alone class that extends ",(0,s.jsx)(n.code,{children:"HTMLElement"}),". The output is a standardized custom element with the styles already attached and without any of Stencil's lazy-loading. This may be preferred for projects that are already handling bundling, lazy-loading and defining the custom elements themselves."]}),"\n",(0,s.jsxs)(n.p,{children:["The generated files will each export a component class and will already have the styles bundled. However, this build does not define the custom elements or apply any polyfills. Static assets referenced within components will need to be set using ",(0,s.jsx)(n.code,{children:"setAssetPath"})," (see ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements#making-assets-available",children:"Making Assets Available"}),")."]}),"\n",(0,s.jsx)(n.p,{children:"You can use these standalone components by importing them via:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import { MyComponent, defineCustomElementMyComponent } from 'my-design-system'\n\n// register to CustomElementRegistry\ndefineCustomElementMyComponent()\n\n// or extend custom element via\nclass MyCustomComponent extends MyComponent {\n  // ...\n}\ndefine('my-custom-component', MyCustomComponent)\n"})}),"\n",(0,s.jsxs)(n.p,{children:["To ensure that the right entry file is loaded when importing the project, define different ",(0,s.jsx)(n.a,{href:"https://nodejs.org/api/packages.html#exports",children:"exports fields"})," in your ",(0,s.jsx)(n.code,{children:"package.json"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n  "exports": {\n    ".": {\n      "import": "./dist/components/index.js",\n      "types": "./dist/components/index.d.ts"\n    },\n    "./my-component": {\n      "import": "./dist/components/my-component.js",\n      "types": "./dist/components/my-component.d.ts"\n    }\n  },\n  "types": "dist/components/index.d.ts",\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"This allows us to map certain import paths to specific components within our project and allows users to only import the component code they are interested in and reduce the amount of code that needs to downloaded by the browser, e.g.:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"// this import loads all compiled components\nimport { MyComponent } from 'my-design-system'\n// only import compiled code for MyComponent\nimport { MyComponent } from 'my-design-system/my-component'\n"})}),"\n",(0,s.jsxs)(n.p,{children:["If you define exports targets for all your components as shown above and by using ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements#customelementsexportbehavior",children:(0,s.jsx)(n.code,{children:"customElementsExportBehavior: 'auto-define-custom-elements'"})})," as output target option, you can skip the ",(0,s.jsx)(n.code,{children:"defineCustomElement"})," call and directly import the component where you need it:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ts",children:"import 'my-design-system/my-component'\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["If you are distributing both the ",(0,s.jsx)(n.code,{children:"dist"})," and ",(0,s.jsx)(n.code,{children:"dist-custom-elements"}
1),", then it's best to pick one of them as the main entry depending on which use case is more prominent."]})}),"\n",(0,s.jsxs)(n.p,{children:["Read more about various options when it comes to distributing your components as standalone components in the ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements",children:(0,s.jsx)(n.code,{children:"dist-custom-elements"})})," output target section."]}),"\n",(0,s.jsxs)(n.p,{children:["The output directory will also contain an ",(0,s.jsx)(n.code,{children:"index.js"})," file which exports some helper methods by default. The contents of the file will look something like:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-js",children:"export { setAssetPath, setPlatformOptions } from '@stencil/core/internal/client';\n"})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["The contents may look different if ",(0,s.jsx)(n.a,{href:"/docs/v4.41/custom-elements#customelementsexportbehavior",children:(0,s.jsx)(n.code,{children:"customElementsExportBehavior"})})," is specified!"]})}),"\n",(0,s.jsx)(n.h4,{id:"considerations-1",children:"Considerations"}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"dist-custom-elements"})," is a direct build of the custom element that extends ",(0,s.jsx)(n.code,{children:"HTMLElement"}),", without any lazy-loading. This distribution strategy may be preferred for projects that use an external bundler such as ",(0,s.jsx)(n.a,{href:"https://vitejs.dev/",children:"Vite"}),", ",(0,s.jsx)(n.a,{href:"https://webpack.js.org/",children:"WebPack"})," or ",(0,s.jsx)(n.a,{href:"https://rollupjs.org",children:"Rollup"})," to compile the application. They ensure that only the components used within your application are bundled into compilation."]}),"\n",(0,s.jsx)(n.h4,{id:"usage-in-typescript",children:"Usage in TypeScript"}),"\n",(0,s.jsxs)(n.p,{children:["If you plan to support consuming your component library in TypeScript you'll need to set ",(0,s.jsx)(n.code,{children:"generateTypeDeclarations: true"})," on the output target in your ",(0,s.jsx)(n.code,{children:"stencil.config.ts"}),", like so:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-tsx",metastring:'title="stencil.config.ts"',children:"import { Config } from '@stencil/core';\n\nexport const config: Config = {\n  outputTargets: [\n    {\n      type: 'dist-custom-elements',\n      generateTypeDeclarations: true,\n    },\n    // ...\n  ],\n  // ...\n};\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Then you can set the ",(0,s.jsx)(n.code,{children:"types"})," property in ",(0,s.jsx)(n.code,{children:"package.json"})," so that consumers of your package can find the type definitions, like so:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",metastring:'title="package.json"',children:'{\n  "types": "dist/components/index.d.ts",\n  "dependencies": {\n    "@stencil/core": "latest"\n  },\n  ...\n}\n'})}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["If you set the ",(0,s.jsx)(n.code,{children:"dir"})," property on the output target config, replace ",(0,s.jsx)(n.code,{children:"dist/components"})," in the above snippet with the path set in the config."]})}),"\n",(0,s.jsx)(n.h2,{id:"publishing-to-npm",children:"Publishing to NPM"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://www.npmjs.com/",children:"NPM"})," is an online software registry for sharing libraries, tools, utilities, packages, etc. To make your Stencil project widely available to be consumed, it's recommended to ",(0,s.jsx)(n.a,{href:"https://docs.npmjs.com/getting-started/publishing-npm-packages",children:"publish the component library to NPM"}),". Once the library is published to NPM, other projects are able to add your component library as a dependency and use the components within their own projects."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},28453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>a});var s=t(96540);const o={},i=s.createContext(o);function r(e){const n=s.useContext(i);return s.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:r(e.components),s.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.