[{"data":1,"prerenderedAt":296},["ShallowReactive",2],{"DefaultLayouten":3,"language-blog-slug-turning-vue-components-into-reusable-npm-packages-i18n-slugs":134,"language-blog-slug-en-turning-vue-components-into-reusable-npm-packages":138},{"app":4,"menu":31,"footer":66},{"githubUrl":5,"youtubeUrl":6,"linkedinUrl":7,"phoneNumber":8,"emailAddress":9,"legal":10,"addresses":20},"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002F","https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUCzHuhQVYFRixtQN2-swcuGg","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fde-voorhoede","+31 20 2610 954","post@voorhoede.nl",[11,14,17],{"title":12,"value":13},"KvK","56017235",{"title":15,"value":16},"BTW","NL851944620B01",{"title":18,"value":19},"IBAN","NL14TRIO0320142078",[21,26],{"address":22,"city":23,"googleMapsLink":24,"postalCode":25},"Koivistokade 70","Amsterdam","https:\u002F\u002Fwww.google.com\u002Fmaps\u002Fplace\u002FDe+Voorhoede+%7C+Front-end+Development\u002F@52.396847,4.8700823,17z\u002Fdata=!3m1!4b1!4m5!3m4!1s0x47c5e21d502d2d59:0xbf570944a96ebf45!8m2!3d52.347647!4d4.8502154","1013 BB",{"address":27,"city":28,"googleMapsLink":29,"postalCode":30},"Koornmarkt 22","Delft","https:\u002F\u002Fwww.google.nl\u002Fmaps\u002Fplace\u002FKoornmarkt+22,+2611+EG+Delft\u002F@52.0093477,4.3573054,17z\u002F","2611 EG",{"title":32,"callToActions":33,"links":39},"Site Menu",[34],{"id":35,"title":36,"link":37},"163140902","Contact",{"__typename":38},"ContactRecord",[40,46,51,56,61],{"id":41,"title":42,"link":43},"163140904","Impact",{"__typename":44,"slug":45},"PageRecord","impact",{"id":47,"title":48,"link":49},"163140905","Services",{"__typename":50},"ServiceOverviewRecord",{"id":52,"title":53,"link":54},"163140906","Cases",{"__typename":55},"CaseOverviewRecord",{"id":57,"title":58,"link":59},"163140908","About us",{"__typename":44,"slug":60},"about-us",{"id":62,"title":63,"link":64},"d6WdFJq2SOuc3dWtpibbXQ","Work at",{"__typename":44,"slug":65},"work-at",{"links":67,"copyrightTitle":93,"copyrightLabel":94,"copyrightLink":95,"privacyTitle":96,"privacyLabel":97,"privacyLink":98,"certificatesGrid":99},[68,71,74,77,82,85,88],{"id":69,"title":42,"link":70},"144185264",{"__typename":44,"slug":45},{"id":72,"title":48,"link":73},"144185265",{"__typename":50},{"id":75,"title":53,"link":76},"144185266",{"__typename":55},{"id":78,"title":79,"link":80},"144185267","Blog",{"__typename":81},"BlogPostOverviewRecord",{"id":83,"title":58,"link":84},"144185268",{"__typename":44,"slug":60},{"id":86,"title":36,"link":87},"144185269",{"__typename":38},{"id":89,"title":90,"link":91},"144185270","FAQ",{"__typename":44,"slug":92},"faq","Creative Commons licence and disclaimer","CC BY 4.0","https:\u002F\u002Fcreativecommons.org\u002Flicenses\u002Fby\u002F4.0\u002F","De Voorhoede privacy statement (pdf)","Privacy statement","https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1763455455-vh-isms-006-privacy-statement-de-voorhoede-en.pdf",[100,112,123],{"id":101,"image":102,"link":107},"Xq4bBfg_TZ6Fkjax9mkbLQ",{"url":103,"alt":104,"width":105,"height":106},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353463-b-corp-logo-black-rgb.png","B Corp logo",404,680,{"__typename":108,"id":109,"title":110,"url":111},"ExternalLinkRecord","fGW1ak8XQYaYDLkBSyncog","B Corp","https:\u002F\u002Fwww.bcorporation.net\u002Fen-us\u002Ffind-a-b-corp\u002Fcompany\u002Fde-voorhoede\u002F",{"id":113,"image":114,"link":119},"c5mCXRTiSraRIB25fw1p7Q",{"url":115,"alt":116,"width":117,"height":118},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353461-dda-boxlogo-black.png","Dutch Digital Agencies logo",627,480,{"__typename":108,"id":120,"title":121,"url":122},"P6Jh7B0cTv2cKyNEeKVWVQ","Dutch Digital Agencies","https:\u002F\u002Fdutchdigitalagencies.com\u002Fleden\u002Fde-voorhoede\u002F",{"id":124,"image":125,"link":129},"MT5SCyNxSTSr_v5eeATMZw",{"url":126,"alt":127,"width":128,"height":128},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1775730283-dnv.png","DNV logo",518,{"id":130,"title":131,"link":132},"BRtNB5HnT5i-7HkA8IYzBw","DIV",{"__typename":44,"slug":133},"impact\u002Fdigitale-producten-privacy-by-design",[135],{"locale":136,"value":137},"en","turning-vue-components-into-reusable-npm-packages",{"page":139},{"slug":137,"i18nSlugs":140,"social":142,"title":143,"subtitle":79,"isArchived":147,"headerIllustration":148,"date":149,"authors":150,"introTitle":159,"items":160,"pivots":283,"relatedBlogPosts":294,"tags":295,"onMountedScript":164,"onUnmountedScript":164},[141],{"locale":136,"value":137},{"title":143,"description":144,"image":145},"Turning Vue components into reusable npm packages","How can you reuse Vue.js components across your projects? \nHere’s how we automated our process to bundle, test, document and publish Vue components.",{"url":146},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1587478158-blogpost-diagram.png",false,null,"2020-04-22T02:00:00.000+02:00",[151],{"name":152,"lastName":153,"slug":154,"image":155},"Sjoerd","Beentjes","sjoerd",{"url":156,"alt":148,"width":157,"height":158},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683534892-sjoerd.jpg",1637,2182,"A guide to publishing your Vue components",[161,166,176,179,183,188,191,195,198,201,204,207,211,218,221,224,227,230,233,238,241,244,247,251,254,257,260,264,267,272,275,279],{"__typename":162,"id":163,"title":164,"body":165},"TextSectionRecord","4586257","","\u003Cp>When working on multiple projects with Vue.js as your framework of choice, you often find yourself writing the same components over and over again. Is there a better way?\u003C\u002Fp>",{"__typename":167,"id":168,"image":169,"caption":171,"fullWidth":174,"captionPosition":175},"ImageRecord","4586258",{"url":170,"alt":171,"width":172,"height":173},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1587476645-blogpost-diagram.png","Workflow using Vue, Vuepress, GitHub and npm",1686,808,true,"bottom",{"__typename":162,"id":177,"title":164,"body":178},"4590172","\u003Cp>A way to solve this is to separate them from your projects and publish them to npm. This way you can make your components reusable, open source and available across teams. Here&rsquo;s how we automated our process to bundle, test, document and publish Vue components.\u003C\u002Fp>\u003Cblockquote class=\"blockquote--minimal\">\u003Cp>Do you want to become a Vue master? During our two day hands-on workshop we&rsquo;ll teach you everything you need to know to build large performant web apps with Vue.\u003C\u002Fp>\u003Cp>\u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fvuemaster\">Join our Vue masterclass\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fblockquote>",{"__typename":162,"id":180,"title":181,"body":182},"4586259","Bundling a Vue component","\u003Cp>One way to share your component is by directly publishing the \u003Ccode>.vue\u003C\u002Fcode> file to npm. This will work in cases where you can import \u003Ccode>.vue\u003C\u002Fcode> files in your project, but what if someone wanted to use it directly in the browser? That will not work. To prepare for these types of situations you should bundle them into \u003Ccode>.js\u003C\u002Fcode> files.\u003C\u002Fp>\u003Cp>As a base for the project we&rsquo;ll need a \u003Ccode>package.json\u003C\u002Fcode> file (run \u003Ccode>npm init\u003C\u002Fcode> to create one) and a \u003Ccode>src\u003C\u002Fcode> folder. &nbsp;The structure of it will roughly look like this:\u003C\u002Fp>",{"__typename":184,"id":185,"language":186,"body":187},"CodeBlockRecord","4586260","markup","src\u002F\n  index.js\n  your-vue-component.vue\n  your-vue-component.test.js\npackage.json\nrollup.config.js",{"__typename":162,"id":189,"title":164,"body":190},"4586261","\u003Cp>For unit testing we&rsquo;re using \u003Ca href=\"https:\u002F\u002Fvue-test-utils.vuejs.org\u002F\">vue-test-utils\u003C\u002Fa>, the official test library for Vue components, combined with \u003Ca href=\"https:\u002F\u002Fjestjs.io\u002F\">Jest\u003C\u002Fa>. Tests are located in \u003Ccode>.test.js\u003C\u002Fcode> files and are used to make sure components work the way they are supposed to work. It improves maintainability, since it will indicate if you broke something when making changes to your code.\u003C\u002Fp>\u003Cp>A commonly used tool for bundling Vue components is Rollup, a JavaScript module bundler. It compiles the Vue components into different formats to use in ES, common JS or directly in the browser. The base configuration for Rollup in this situation looks like this:\u003C\u002Fp>",{"__typename":184,"id":192,"language":193,"body":194},"4586262","javascript","\u002F\u002F import necessary dependencies\nimport vue from 'rollup-plugin-vue'\nimport buble from 'rollup-plugin-buble'\nimport commonjs from 'rollup-plugin-commonjs'\n\nexport default {\n  input: 'src\u002Findex.js', \u002F\u002F entry file for our components\n  plugins: {\n    preVue: [\n      replace({\n        'process.env.NODE_ENV': JSON.stringify('production'),\n      }),\n      commonjs(), \u002F\u002F add support for CommonJS modules\n    ],\n    vue: {\n      css: true, \u002F\u002F include CSS in the output\n      template: {\n        isProduction: true,\n      },\n    },\n    postVue: [\n      buble(), \u002F\u002F use buble to transpile ES2015\n    ],\n  },\n}",{"__typename":162,"id":196,"title":164,"body":197},"4586263","\u003Cp>This base configuration can now be extended for the module types we want to export. For a full example, see the \u003Ccode>\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fvue-lazy-load\u002Fblob\u002Fmaster\u002Frollup.config.js\">rollup.config.js\u003C\u002Fa>\u003C\u002Fcode> of our vue-lazy-load component.\u003C\u002Fp>\u003Cp>In our \u003Ccode>package.json\u003C\u002Fcode> we can specify where we want our transpiled bundles to end up:\u003C\u002Fp>",{"__typename":184,"id":199,"language":193,"body":200},"4586264","{\n  \"name\": \"your-component-name\",\n  \"main\": \"dist\u002Fyour-component-name.ssr.js\",\n  \"module\": \"dist\u002Fyour-component-name.esm.js\",\n  \"unpkg\": \"dist\u002Fyour-component-name.min.js\",\n  \"style\": \"dist\u002Fyour-component-name.css\",\n  \u002F\u002F ...\n}",{"__typename":162,"id":202,"title":164,"body":203},"4586265","\u003Cp>All the different fields serve their purpose:\u003C\u002Fp>\u003Cul>\u003Cli>Modern bundlers will use the \u003Ccode>module\u003C\u002Fcode> build\u003C\u002Fli>\u003Cli>Legacy bundlers and Node.js will use the \u003Ccode>main\u003C\u002Fcode> build\u003C\u002Fli>\u003Cli>The \u003Ccode>unpkg\u003C\u002Fcode> bundle can be used directly in the browser via \u003Ca href=\"https:\u002F\u002Funpkg.com\u002F\">UNPKG\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>You&rsquo;ll also notice a \u003Ccode>style\u003C\u002Fcode> property. Typically, you&rsquo;ll want to separate your styles from the JS bundle. This gives freedom for users to apply their styles or use their loaders\u002Fplugins of choice to process them.\u003C\u002Fp>\u003Cp>This is an example of how you would import the component and styles in your application:\u003C\u002Fp>",{"__typename":184,"id":205,"language":193,"body":206},"4586266","import YourComponentName from 'your-component-name'\nimport 'your-component-name\u002Fdist\u002Fyour-component-name.css'",{"__typename":162,"id":208,"title":209,"body":210},"4586267","Adding documentation with VuePress","\u003Cp>Writing your documentation in your \u003Ccode>readme.md\u003C\u002Fcode> is a convenient way to document components. But what if you want to create an interactive demo or add a nice theme to it? Markdown processing tools like \u003Ca href=\"https:\u002F\u002Fvuepress.vuejs.org\u002F\">VuePress\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fwww.docz.site\u002F\">Docz\u003C\u002Fa> are a perfect option for that. These tools convert markdown files into a static site to host as your documentation. In our case, we&rsquo;re using VuePress, since it suits the Vue ecosystem.\u003C\u002Fp>",{"__typename":167,"id":212,"image":213,"caption":215,"fullWidth":147,"captionPosition":175},"4586268",{"url":214,"alt":215,"width":216,"height":217},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1587482520-docs-1.png","Component documented with VuePress",2464,1756,{"__typename":162,"id":219,"title":164,"body":220},"4586269","\u003Cp>To create our documentation we need a folder to build it from. The structure for this folder should look something like this:\u003C\u002Fp>",{"__typename":184,"id":222,"language":186,"body":223},"4586270","docs\u002F\n  readme.md\n  .vuepress\u002F\n    config.js",{"__typename":162,"id":225,"title":164,"body":226},"4586271","\u003Cp>The \u003Ccode>readme.md\u003C\u002Fcode> file is used for generating the HTML. There are plugins available for VuePress to extend its functionality. In our case we&rsquo;re using \u003Ca href=\"https:\u002F\u002Fvuepress.vuejs.org\u002Fplugin\u002Fofficial\u002Fplugin-register-components.html\">plugin-register-components\u003C\u002Fa> combined with \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FBuptStEve\u002Fvuepress-plugin-demo-code\">vuepress-plugin-demo-code\u003C\u002Fa> to be able to demonstrate our components:\u003C\u002Fp>",{"__typename":184,"id":228,"language":193,"body":229},"4586272","const pkg = require('..\u002F..\u002Fpackage.json')\n\nmodule.exports = {\n  title: `${pkg.name} (${pkg.version})`,\n  description: pkg.description,\n  dest: `www`,\n  plugins: [\n    ['@vuepress\u002Fregister-components', {\n        componentsDir: `${__dirname}\u002F..\u002F..\u002Fsrc\u002F`\n    }],\n    'demo-code'\n  ]\n}",{"__typename":162,"id":231,"title":164,"body":232},"4586273","\u003Ch3>\u003Cstrong>Documenting Vue props, events and slots\u003C\u002Fstrong>\u003C\u002Fh3>\u003Cp>To give the user information about our component, we should document props, events and slots. Documenting those is not fun to do by hand and seems like something we can automate. A tool named \u003Ca href=\"https:\u002F\u002Fgitlab.com\u002Fvuedoc\u002Fmd\">@vuedoc\u002Fmd\u003C\u002Fa> can do just this. It scans through the Vue component looking for props, slots, and events. It converts these into a markdown structure and will append it to a specified markdown file.\u003C\u002Fp>",{"__typename":167,"id":234,"image":235,"caption":237,"fullWidth":147,"captionPosition":175},"4586274",{"url":236,"alt":237,"width":216,"height":217},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1587482495-docs-2.png","Output of generated docs",{"__typename":162,"id":239,"title":164,"body":240},"4586275","\u003Cp>Below are the scripts to generate our documentation. First, we extract the props, events, and slots and append to our markdown file. After that, we generate our static site using VuePress.\u003C\u002Fp>",{"__typename":184,"id":242,"language":193,"body":243},"4586276","\"scripts\": {\n  \u002F\u002F ...\n  \"docs\": \"run-s docs:*\",\n  \"docs:api\": \"vuedoc.md src\u002Fyour-component-name.vue --section 'API' --output docs\u002Fv1\u002Freadme.md --ignore-data --ignore-methods --ignore-computed\",\n  \"docs:vuepress\": \"vuepress build docs\",\n  \u002F\u002F ...\n},",{"__typename":162,"id":245,"title":164,"body":246},"4586277","\u003Cp>The generated static site can now be hosted wherever you want, for example on \u003Ca href=\"https:\u002F\u002Fwww.netlify.com\u002F\">Netlify\u003C\u002Fa> or \u003Ca href=\"https:\u002F\u002Fpages.github.com\u002F\">GitHub pages\u003C\u002Fa>.\u003C\u002Fp>",{"__typename":162,"id":248,"title":249,"body":250},"4586278","Publishing to npm","\u003Cp>The bundles created by Rollup are ready to be published to npm. After creating a build, use \u003Ccode>\u003Ca href=\"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fpublish\">npm publish\u003C\u002Fa>\u003C\u002Fcode>.\u003C\u002Fp>",{"__typename":162,"id":252,"title":164,"body":253},"4586286","\u003Cp>To make things easier, we can automate this by using a \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Factions\">GitHub action\u003C\u002Fa>. This will automatically create a new version of your npm package when a new tag is pushed. We do this by adding a \u003Ccode>.github\u002Fworkflows\u002Fnpm.yml\u003C\u002Fcode> file:\u003C\u002Fp>",{"__typename":184,"id":255,"language":186,"body":256},"4586287","on:\n  push:\n    tags: v*.*.*\njobs:\n  npm:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions\u002Fcheckout@master\n    - uses: actions\u002Fsetup-node@v1\n      with:\n        node-version: '10.x'\n        registry-url: 'https:\u002F\u002Fregistry.npmjs.org'\n    - name: Build package\n      run: npm run build\n    - name: Publish tag to npm\n      if: contains(github.ref, 'tags')\n      run: npm publish\n      env:\n        NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}\n",{"__typename":162,"id":258,"title":164,"body":259},"4586288","\u003Cp>Here&rsquo;s the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fvue-dato-image\u002Fblob\u002Fmaster\u002F.github\u002Fworkflows\u002Fnpm.yml\">complete workflow for one of our Vue components\u003C\u002Fa>.\u003C\u002Fp>",{"__typename":162,"id":261,"title":262,"body":263},"4586289","Bonus: publish to GitHub","\u003Cp>It&rsquo;s also possible to publish your package to \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Fpackages\">GitHub packages\u003C\u002Fa> registry\u003C\u002Fp>",{"__typename":184,"id":265,"language":186,"body":266},"4586293","- uses: actions\u002Fsetup-node@v1\n  with:\n    registry-url: 'https:\u002F\u002Fnpm.pkg.github.com'\n\n- name: Publish tag to GPR\n  run: npm publish\n  env:\n    NODE_AUTH_TOKEN: ${{ secrets.GITHUB_AUTH_TOKEN }}k\n",{"__typename":167,"id":268,"image":269,"caption":271,"fullWidth":147,"captionPosition":175},"4586294",{"url":270,"alt":271,"width":216,"height":217},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1587482480-github.png","Package on GitHub",{"__typename":162,"id":273,"title":164,"body":274},"4586302","\u003Cp>You now have an npm package with (partly) automated documentation and publishing to npm on every new tag! To see an example of how this is implemented in real-life, take a look at the repo of \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fvue-lazy-load\">@voorhoede\u002Fvue-lazy-load\u003C\u002Fa>. We have created a couple of components this way, those can be found on our \u003Ca href=\"https:\u002F\u002Fvoorhoede.github.io\u002F#front-end-components\">open-source page\u003C\u002Fa>. If you publish a Vue component yourself, please do share it with us!\u003C\u002Fp>",{"__typename":162,"id":276,"title":277,"body":278},"4586303","Resources","\u003Cul>\u003Cli>\u003Ca href=\"https:\u002F\u002Fvuejs.org\u002Fv2\u002Fcookbook\u002Fpackaging-sfc-for-npm.html\">Packaging Vue Components for npm\u003C\u002Fa>\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fdocs.npmjs.com\u002Fpackages-and-modules\u002Fcontributing-packages-to-the-registry\">Contributing packages to the registry\u003C\u002Fa>\u003C\u002Fli>\u003Cli>\u003Ca href=\"https:\u002F\u002Fhelp.github.com\u002Fen\u002Factions\u002Flanguage-and-framework-guides\u002Fpublishing-nodejs-packages\">Publishing Node.js packages\u003C\u002Fa>\u003C\u002Fli>\u003C\u002Ful>",{"__typename":162,"id":280,"title":281,"body":282},"148387255","More about Vue","\u003Cul>\n  \u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fnl\u002Fblog\u002Fhighlights-vue-js-nation-2023\u002F\">Highlights from Vue.js Nation 2023\u003C\u002Fa>\u003C\u002Fli>\n  \u003Cli>Looking for experienced \u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fen\u002Fservices\u002Fexperienced-vuejs-developers\u002F\">Vue.js developers\u003C\u002Fa>? We can help!\u003C\u002Fli>\n\u003C\u002Ful>",[284],{"title":285,"body":286,"links":287,"mailchimpValue":164,"mailchimpName":164,"mailchimpId":164,"formType":164,"contactPerson":148},"Also in love with the web?","\u003Cp>For us, that’s about technology and user experience. Fast, available for all, enjoyable to use. And fun to build. This is how our team bands together, adhering to the same values, to make sure we achieve a solid result for clients both large and small. Does that fit you?\u003C\u002Fp>\n",[288],{"__typename":289,"id":290,"title":291,"link":292},"InternalLinkRecord","163140992","Join our team",{"__typename":44,"slug":293},"jobs",[],[],1776256146087]