[{"data":1,"prerenderedAt":289},["ShallowReactive",2],{"DefaultLayouten":3,"language-blog-slug-javascript-frameworks-meet-web-components-i18n-slugs":134,"language-blog-slug-en-javascript-frameworks-meet-web-components":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","javascript-frameworks-meet-web-components",{"page":139},{"slug":137,"i18nSlugs":140,"social":142,"title":146,"subtitle":79,"isArchived":147,"headerIllustration":145,"date":148,"authors":149,"introTitle":166,"items":167,"pivots":276,"relatedBlogPosts":287,"tags":288,"onMountedScript":152,"onUnmountedScript":152},[141],{"locale":136,"value":137},{"title":143,"description":144,"image":145},"JavaScript frameworks, meet Web Components  | De Voorhoede","We did an experiment: create and consume a Web Component using a selection of JavaScript frameworks. In this post we'll share our experience and our verdict.",null,"JavaScript frameworks, meet Web Components",false,"2018-12-17T01:00:00.000+01:00",[150,158],{"name":151,"lastName":152,"slug":153,"image":154},"Vincent","","vincent",{"url":155,"alt":145,"width":156,"height":157},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683535345-vincent.jpg",2252,3003,{"name":159,"lastName":160,"slug":161,"image":162},"Peter","Goes","peter",{"url":163,"alt":145,"width":164,"height":165},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1744374527-peter-edit.jpg",1381,1839,"Experimenting with Web Components in popular JavaScript frameworks",[168,172,184,187,191,196,199,203,206,209,213,216,219,223,226,229,233,236,239,243,247,250,254,257,260,264,273],{"__typename":169,"id":170,"title":152,"body":171},"TextSectionRecord","586498","\u003Cp>We worked with many JavaScript frameworks over the years - from Angular, React and Vue to the smaller Preact, Riot and Svelte. But these frameworks don't really play well together. So we can't easily use our React components in our Vue apps or vice versa. Enter \u003Cstrong>Web Components\u003C\u002Fstrong>, a new player on the web promising to solve \u003Ca href=\"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FInteroperability\" target=\"_blank\" rel=\"noopener\">interoperability\u003C\u002Fa>. Define your own HTML elements, for instance a \u003Ccode>&lt;hue-slider&gt;\u003C\u002Fcode> and use it wherever you like:\u003C\u002Fp>",{"__typename":173,"id":174,"mute":175,"loop":175,"autoplay":175,"caption":152,"video":176,"gif":145},"ResponsiveVideoRecord","590114",true,{"url":177,"title":178,"height":179,"width":180,"provider":181,"providerUid":182,"thumbnailUrl":183},"https:\u002F\u002Fvimeo.com\u002F307273930\u002F1940080086","Hue-slider web component",360,626,"vimeo","307273930","https:\u002F\u002Fi.vimeocdn.com\u002Fvideo\u002F747200208-4a4f2833e16ab72618535ba8d90462731a96dc5cbe14c055c26655ed2dbea8f0-d_295x166",{"__typename":169,"id":185,"title":152,"body":186},"590115","\u003Cp>We want to know if Web Components live up to their promise. So we did an experiment: \u003Cstrong>create and consume a Web Component using a selection of JavaScript frameworks\u003C\u002Fstrong>. In this post we'll share our experience and our verdict. Spoiler: Web Components are indeed very promising and we'll try to find a suitable project for them.\u003C\u002Fp>\n\u003Cp>We'll be creating a hue slider to test the core Web Component features:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Pass values using \u003Cstrong>custom attributes\u003C\u002Fstrong>:\u003Cbr \u002F>\u003Ccode>&lt;hue-slider value=\"...\"&gt;\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Listen to \u003Cstrong>custom events\u003C\u002Fstrong>:\u003Cbr \u002F>\u003Ccode>document.querySelector('hue-slider').addEventListener('hueChange', ...)\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>Use \u003Cstrong>slots\u003C\u002Fstrong> to transclude content:\u003Cbr \u002F>\u003Ccode>&lt;hue-slider&gt;content&lt;\u002Fhue-slider&gt;\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>For our experiment we selected the following frameworks: \u003Ca href=\"https:\u002F\u002Fwww.polymer-project.org\u002F\" target=\"_blank\" rel=\"noopener\">Polymer\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Freactjs.org\" target=\"_blank\" rel=\"noopener\">React\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fskatejs.netlify.com\" target=\"_blank\" rel=\"noopener\">SkateJS\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fstenciljs.com\" target=\"_blank\" rel=\"noopener\">Stencil\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fsvelte.technology\" target=\"_blank\" rel=\"noopener\">Svelte\u003C\u002Fa> and \u003Ca href=\"https:\u002F\u002Fvuejs.org\" target=\"_blank\" rel=\"noopener\">Vue.js\u003C\u002Fa>. As a baseline however we'll start by creating our Web Component using vanilla JavaScript.\u003C\u002Fp>",{"__typename":169,"id":188,"title":189,"body":190},"586509","Web Component using Vanilla JS","\u003Cp>Building a native web component (without any framework) is really fun to do! Since you are writing plain JavaScript, it feels like a mix of old school event handlers with new school ES6 goodness. It is also quite educational as you learn exactly how a web component works. But frameworks exists for a reason, it's very labor intensive and difficult to transpile back to ES5.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":193,"language":194,"body":195},"CodeBlockRecord","586510","javascript","class HueSlider extends HTMLElement {\n  constructor() {\n    super()\n    const shadowRoot = this.attachShadow({ mode: 'open' })\n    shadowRoot.innerHTML = `\n     \u003Clabel>\n        \u003Cinput type=\"range\" min=\"0\" max=\"360\">\n        \u003Coutput>\u003Cslot>\u003C\u002Fslot>\u003C\u002Foutput>\n      \u003C\u002Flabel>\n    `\n  }\n  static get observedAttributes() { return ['hue'] }\n  attributeChangedCallback() {  \u002F* ... *\u002F }\n  handleInput (event) {\n    event.stopPropagation()\n    this.dispatchEvent(new CustomEvent('hueChange', this.input.value))\n  }\n}",{"__typename":169,"id":197,"title":152,"body":198},"586642","\u003Cul>\n\u003Cli>Handle custom attributes using\u003Cstrong>\u003Ccode>observedAttributes\u003C\u002Fcode>\u003C\u002Fstrong> and \u003Cstrong>\u003Ccode>attributeChangedCallback\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Emit event using \u003Cstrong>\u003Ccode>dispatchEvent(new CustomEvent())\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Slotted content using \u003Cstrong>\u003Ccode>&lt;slot&gt;&lt;\u002Fslot&gt;\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Fnative\" target=\"_blank\" rel=\"noopener\">native component source\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Smallest possible file size\u003C\u002Fli>\n\u003Cli>Close-to-the-metal control over your component\u003C\u002Fli>\n\u003Cli>Vanilla JavaScript\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Labor intensive\u003C\u002Fli>\n\u003Cli>Do-everything-yourself, get nothing for free\u003C\u002Fli>\n\u003Cli>Transpilation is difficult\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":200,"title":201,"body":202},"586601","Polymer \u002F LitElement","\u003Cp>The \u003Ca href=\"https:\u002F\u002Fwww.polymer-project.org\u002F\" target=\"_blank\" rel=\"noopener\">Polymer Project\u003C\u002Fa> created \u003Ca href=\"https:\u002F\u002Flit-element.polymer-project.org\u002F\" target=\"_blank\" rel=\"noopener\">LitElement\u003C\u002Fa> \"A simple base class for creating fast, lightweight web components\". Together with the polymer-cli this creates a powerful combination. LitElement is a small base class which gives you the close-to-the-metal feeling but with a reactive renderer.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":204,"language":194,"body":205},"586608","class HueSlider extends LitElement {\n  static get properties() {\n    return { hue: { type: String } }\n  }\n  constructor() {  \u002F* ... *\u002F }\n  handleChange(event) {\n    \u002F* ... *\u002F\n    this.dispatchEvent(new CustomEvent('hueChange', this.hue)) \n  }\n  render() {\n    return html`\n      \u003Clabel>\n        \u003Cinput type=\"range\" min=\"0\" max=\"360\" value=${this.hue} @change=${this.handleChange} \u002F>\n        \u003Coutput style=\"background-color: hsl(${this.hue}, 100%, 50%);\">\n          \u003Cslot>\u003C\u002Fslot>\n        \u003C\u002Foutput>\n      \u003C\u002Flabel>\n    `\n  }\n}",{"__typename":169,"id":207,"title":152,"body":208},"586662","\u003Cul>\n\u003Cli>Handle custom attributes using\u003Cstrong>&nbsp;\u003Ccode>get properties\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Emit custom event using&nbsp;\u003Cstrong>\u003Ccode>dispatchEvent(new CustomEvent())\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Slotted content using&nbsp;\u003Cstrong>\u003Ccode>&lt;slot&gt;&lt;\u002Fslot&gt;\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Fblob\u002Fmaster\u002Fsource\u002Fpolymer\u002Fsrc\u002Fcomponents\u002Fhue-slider-polymer.js\">Polymer component source\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Close-to-the-metal but without the labor intensiveness of going full native\u003C\u002Fli>\n\u003Cli>Great tooling\u003C\u002Fli>\n\u003Cli>It's easy to create separate bundles for different browser capabilities\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Not as feature rich as most front-end frameworks\u003C\u002Fli>\n\u003Cli>Combination of separate projects, all with their own documentation\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":210,"title":211,"body":212},"586609","React","\u003Cp>For the \u003Ca href=\"https:\u002F\u002Freactjs.org\" target=\"_blank\" rel=\"noopener\">React\u003C\u002Fa> version of our component we used \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fcreate-react-app\" target=\"_blank\" rel=\"noopener\">Create React App\u003C\u002Fa> to quickly get going. We had to use the \u003Ca href=\"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Freact-web-component\" target=\"_blank\" rel=\"noopener\">react-web-component\u003C\u002Fa> package to wrap our React component into a web component. A side-effect of using React for building web components is that it is included in the resulting output bundle and requires you to have React as an external dependency on the page.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":214,"language":194,"body":215},"586610","export default class HueSlider extends React.Component {\n  constructor(props) { \u002F* ... *\u002F }\n  handleChange = () => { \u002F* ... *\u002F }\n  webComponentConstructed() { \u002F* ... *\u002F }\n  render() {\n    const { hue } = this.state\n    return (\n     \u003Clabel>\n        \u003Cinput type=\"range\" min=\"0\" max=\"360\" value={hue} onChange={this.handleChange} \u002F>\n        \u003Coutput style={{ backgroundColor: `hsl(${hue}, 100%, 50%)` }}>\n          { this.props.children }\n        \u003C\u002Foutput>\n      \u003C\u002Flabel>\n    )\n  }\n}",{"__typename":169,"id":217,"title":152,"body":218},"586663","\u003Cul>\n\u003Cli>Handle custom attributes using \u003Cstrong>\u003Ccode>props\u003C\u002Fcode>\u003C\u002Fstrong> and \u003Cstrong>\u003Ccode>webComponentConstructed\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Slotted content using \u003Cstrong>\u003Ccode>this.props.children\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Freact\" target=\"_blank\" rel=\"noopener\">React component source\u003C\u002Fa>. And \u003Ca href=\"https:\u002F\u002Freactjs.org\u002Fdocs\u002Fweb-components.html\" target=\"_blank\" rel=\"noopener\">read more about React and web components here\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Familiar syntax\u003C\u002Fli>\n\u003Cli>Little additional build steps required\u003C\u002Fli>\n\u003Cli>React as a framework is very mature\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>No build-in support to create and export web components\u003C\u002Fli>\n\u003Cli>React required as an external dependency\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":220,"title":221,"body":222},"586612","SkateJS","\u003Cp>SkateJS positions itself as &ldquo;a functional abstraction over the web component standards&rdquo;. It&rsquo;s close-to-the-metal but some things are being taken care of or are abstracted away. One of the options SkateJS offers is what renderer you would like to use. Also, SkateJS isn&rsquo;t that well known yet, so there was only little documentation and it wasn&rsquo;t very extensive.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":224,"language":194,"body":225},"586613","class HueSlider extends withComponent(withPreact()) {\n  static get props() {\n    return {\n      hue: props.string\n    }\n  }\n  connectedCallback() { ... }\n  handleChange = () => { ... }\n  render({ hue, handleChange }) {\n    return (\n      \u003Clabel>\n        \u003Cinput type=\"range\" min=\"0\" max=\"360\" value={hue} onInput={handleChange} \u002F>\n        \u003Coutput style={{ backgroundColor: `hsl(${hue}, 100%, 50%)` }}>\n          \u003Cslot>\u003C\u002Fslot>\n        \u003C\u002Foutput>\n      \u003C\u002Flabel>\n    )\n  }\n}",{"__typename":169,"id":227,"title":152,"body":228},"586664","\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Fskate\" target=\"_blank\" rel=\"noopener\">SkateJS component source\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Close-to-the-metal but without the labor intensiveness of going full native\u003C\u002Fli>\n\u003Cli>Choose your own renderer\u003C\u002Fli>\n\u003Cli>Small file size\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Very little documentation\u002Freading material\u003C\u002Fli>\n\u003Cli>Not as mature as other frameworks\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":230,"title":231,"body":232},"586614","Stencil","\u003Cp>\u003Ca href=\"https:\u002F\u002Fstenciljs.com\" target=\"_blank\" rel=\"noopener\">Stencil\u003C\u002Fa> is a compiler that generates web components. It mixes TypeScript classes with JSX to produce web components. We did not use Stencil before so we needed a bit of getting used to the syntax and build setup.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":234,"language":194,"body":235},"586615","@Component({\n  tag: 'hue-slider-stencil',\n  styleUrl: 'hue-slider-stencil.css',\n  shadow: true\n})\n\nexport class HueSlider {\n  @Prop({ reflectToAttr: true, mutable: true }) hue: string = '100'\n  @State() inputValue: string\n  @State() value: string\n  @Event({ eventName: 'input'}) inputEvent: EventEmitter\n  handleInput() { ... }\n  componentWillLoad() { ... }\n  @Watch('hue')\n  hueUpdated(val) { ... }\n  render() {\n    return (\n      \u003Clabel>\n        \u003Cinput type=\"range\" min=\"0\" max=\"360\" value={`${this.hue}`} onInput={(e) => this.handleInput(e)} \u002F>\n        \u003Coutput style={{ backgroundColor: this.value }}>\n          \u003Cslot>\u003C\u002Fslot>\n        \u003C\u002Foutput>\n      \u003C\u002Flabel>\n    )\n  }\n}",{"__typename":169,"id":237,"title":152,"body":238},"586665","\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Fstencil\" target=\"_blank\" rel=\"noopener\">Stencil component source\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Fully focused on building web components\u003C\u002Fli>\n\u003Cli>A loader build-in to only load polyfills needed\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Not yet mature yet\u003C\u002Fli>\n\u003Cli>Quite a learning curve for those new to TypeScript and JSX\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":240,"title":241,"body":242},"586632","Svelte","\u003Cp>Although we have used \u003Ca href=\"https:\u002F\u002Fsvelte.technology\u002F\" target=\"_blank\" rel=\"noopener\">Svelte\u003C\u002Fa> before, we tend to use Vue more often for our day-to-day work. However, since they share a quite similar API, working with Svelte is much like working with Vue.\u003C\u002Fp>\n\u003Cp>The compiler of Svelte can convert components directly to web components. Having web component support baked into the library.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":244,"language":245,"body":246},"586633","html","\u003Clabel on:input=\"onInput(e)\" on:change=\"onChange(e)\">\n  {#if hue}\n    \u003Cinput type=\"range\" min=\"0\" max=\"360\" value=\"{inputValue}\">\n  {:else}\n    \u003Cinput type=\"range\" min=\"0\" max=\"360\">\n  {\u002Fif}\n  \u003Coutput style=\"background-color: hsl({inputValue}, 100%, 50%)\">\n    \u003Cslot>\u003C\u002Fslot>\n  \u003C\u002Foutput>\n\u003C\u002Flabel>\n\u003Cscript>\n  export default {\n    tag: 'hue-slider-svelte',\n    data() {\n      return {\n        hue: '0',\n        inputValue: '0',\n        value: '0'\n      }\n    },\n    onstate() { ... },\n    onupdate() { ... },\n    oncreate() { ... },\n    methods: {\n      onInput() { ... },\n      onChange() { ... }\n    }\n  }\n\u003C\u002Fscript>",{"__typename":169,"id":248,"title":152,"body":249},"586666","\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Fsvelte\" target=\"_blank\" rel=\"noopener\">Svelte component source\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Familiar syntax (Vue like)\u003C\u002Fli>\n\u003Cli>No additional build steps (it's just a config setting)\u003C\u002Fli>\n\u003Cli>Small file size\u003C\u002Fli>\n\u003Cli>Web component support baked into the library\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Framework slightly less mature \u002F feature rich\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":251,"title":252,"body":253},"586634","Vue.js","\u003Cp>We use \u003Ca href=\"https:\u002F\u002Fvuejs.org\u002F\" target=\"_blank\" rel=\"noopener\">Vue.js\u003C\u002Fa> quite often here at De Voorhoede. So we are comfortable with its syntax and build system. The creation of the component took little effort. To transform the Vue component into a web component, we used the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fvue-web-component-wrapper\" target=\"_blank\" rel=\"noopener\">@vue\u002Fweb-component-wrapper\u003C\u002Fa> library to wrap our Vue component into a web component. This approach requires you to have Vue as an external dependency on the page.\u003C\u002Fp>\n\u003Cp>Source code (simplified):\u003C\u002Fp>",{"__typename":192,"id":255,"language":245,"body":256},"586635","\u003Ctemplate>\n  \u003Clabel>\n    \u003Cinput type=\"range\" min=\"0\" max=\"360\" :value=\"inputValue\" @input.stop=\"onInput\" @change.stop=\"onChange\">\n    \u003Coutput :style=\"`background-color: hsl(${inputValue}, 100%, 50%)`\">\n      \u003Cslot \u002F>\n    \u003C\u002Foutput>\n  \u003C\u002Flabel>\n\u003C\u002Ftemplate>\n\u003Cscript>\n  export default {\n    name: 'HueSlider',\n    props: ['hue'],\n    data: () => ({\n      inputValue: null,\n      value: '0'\n    }),\n    methods: {\n      setValue() { ... },\n      onInput() { ... },\n      onChange() { ... }\n    },\n    mounted() { ... },\n    watch: { ... }\n  }\n\u003C\u002Fscript>",{"__typename":169,"id":258,"title":152,"body":259},"586667","\u003Cul>\n\u003Cli>Bind to custom attributes using \u003Cstrong>\u003Ccode>props\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Emit custom event using \u003Cstrong>\u003Ccode>$emit()\u003C\u002Fcode>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003Cli>Slotted content using \u003Cstrong>\u003Ccode>&lt;slot\u002F&gt;\u003C\u002Fcode>\u003C\u002Fstrong>\u003Cstrong>\u003C\u002Fstrong>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>View the full \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002Fexperiment-web-components\u002Ftree\u002Fmaster\u002Fsource\u002Fvue\" target=\"_blank\" rel=\"noopener\">Vue.js component source\u003C\u002Fa>. And \u003Ca href=\"https:\u002F\u002Fvuejsdevelopers.com\u002F2018\u002F05\u002F21\u002Fvue-js-web-component\u002F\" target=\"_blank\" rel=\"noopener\">read more about Vue.js and web components\u003C\u002Fa>.\u003C\u002Fp>\n\u003Cp>Pros:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Familiar syntax\u003C\u002Fli>\n\u003Cli>Little additional build steps required\u003C\u002Fli>\n\u003Cli>Vue as a framework is very mature\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Cons:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>VueJS required as an external dependency\u003C\u002Fli>\n\u003C\u002Ful>",{"__typename":169,"id":261,"title":262,"body":263},"586668","Using the generated web components","\u003Cp>It&rsquo;s actually quite easy to use web components different frameworks. It mostly boiled down to importing the component and defining it on the page. We could use the web components in Storybook and use some web components in&nbsp;\u003Ca href=\"https:\u002F\u002Fframer.com\u002F\" target=\"_blank\" rel=\"noopener\">Framer X\u003C\u002Fa>, an interactive design tool (still in beta).\u003C\u002Fp>\n\u003Cp>This means that we can develop components and designers can use these as interactive building blocks within their Framer X project. Very cool stuff!\u003C\u002Fp>",{"__typename":173,"id":265,"mute":175,"loop":175,"autoplay":175,"caption":152,"video":266,"gif":145},"624356",{"url":267,"title":268,"height":269,"width":270,"provider":181,"providerUid":271,"thumbnailUrl":272},"https:\u002F\u002Fvimeo.com\u002F311445528","Using a Web Component interactively in Framer X",248,520,"311445528","https:\u002F\u002Fi.vimeocdn.com\u002Fvideo\u002F752251562-f4f26a6db422bcdbc4e639479c58eeb39b310f866cf982959e861f230a70800f-d_295x166",{"__typename":169,"id":274,"title":152,"body":275},"624357","\u003Ch3>Final thoughts\u003C\u002Fh3>\n\u003Cp>The biggest take-away we found was that web components created using Vue and React, had their respective frameworks included in the bundle. Without that, the components would not work. We want to use components without that dependency. Splitting components and framework into separate bundles would be ideal.\u003C\u002Fp>\n\u003Cp>We also need to talk about browser support. Native web components are getting better browser support with each new release and by using polyfills and transpiling we were able to get web components to work in most of the major browsers.\u003C\u002Fp>\n\u003Cdiv>\n\u003Ch3>A note on performance\u003C\u002Fh3>\n\u003Cp>We focussed on compatibility and ease-of-use between frameworks during this experiment. Of course we&rsquo;re also interested in the performance of these frameworks when using web components and the web components they produce. But this will be done in another, more controlled, performance focussed follow-up experiment. Stay tuned...\u003C\u002Fp>\n\u003Cp>Want to start with web components yourself? Check out \u003Ca href=\"https:\u002F\u002Fdevelopers.google.com\u002Fweb\u002Ffundamentals\u002Fweb-components\u002F\" target=\"_blank\" rel=\"noopener\">Web Fundamentals\u003C\u002Fa>, \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FWeb_Components\" target=\"_blank\" rel=\"noopener\">developer.mozilla.org\u003C\u002Fa> and \u003Ca href=\"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=XCti72iChzg\" target=\"_blank\" rel=\"noopener\">this video with Paul Lewis\u003C\u002Fa>.\u003C\u002Fp>\n\u003C\u002Fdiv>",[277],{"title":278,"body":279,"links":280,"mailchimpValue":152,"mailchimpName":152,"mailchimpId":152,"formType":152,"contactPerson":145},"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",[281],{"__typename":282,"id":283,"title":284,"link":285},"InternalLinkRecord","163140992","Join our team",{"__typename":44,"slug":286},"jobs",[],[],1776256145474]