<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Felix Haus</title>
    <description>The latest articles on DEV Community by Felix Haus (@ofhouse).</description>
    <link>https://dev.to/ofhouse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F244702%2F25d44934-65d9-461b-b8e6-43763133a37b.jpg</url>
      <title>DEV Community: Felix Haus</title>
      <link>https://dev.to/ofhouse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ofhouse"/>
    <language>en</language>
    <item>
      <title>Create your own serverless image optimizer for Next.js in 5 minutes</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Sat, 01 May 2021 15:48:34 +0000</pubDate>
      <link>https://dev.to/ofhouse/create-your-own-serverless-image-optimizer-for-next-js-in-5-minutes-aa8</link>
      <guid>https://dev.to/ofhouse/create-your-own-serverless-image-optimizer-for-next-js-in-5-minutes-aa8</guid>
      <description>&lt;p&gt;With the introduction of Next.js 10 the new &lt;a href="https://nextjs.org/docs/api-reference/next/image" rel="noopener noreferrer"&gt;next/image component&lt;/a&gt; was added. It is a modern approach to enhance the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; HTML-Element by adjusting the image based on the viewer’s needs. By recognizing what image extensions (e.g. newer formats like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types" rel="noopener noreferrer"&gt;WebP&lt;/a&gt;) the client browser supports, it converts the source accordingly and also resizes the image optimized for the screen width of the viewer.&lt;/p&gt;

&lt;p&gt;The optimization itself is performed on the fly when the resource is requested by the user. This makes the solution perfectly scalable whether it is used for 10 or 10 million images.&lt;/p&gt;

&lt;p&gt;But what if you self-host your app as a single server or inside a docker container? Spinning up more instances just to deal with the increased traffic caused by image processing can be pretty expensive.&lt;/p&gt;

&lt;p&gt;Fortunately the Next.js developers have a solution for this: You can use a cloud-based &lt;a href="https://nextjs.org/docs/basic-features/image-optimization#loader" rel="noopener noreferrer"&gt;external image loader&lt;/a&gt; to perform this task. Currently Imgix, Cloudinary and Akamai are officially supported.&lt;/p&gt;

&lt;p&gt;Since we were already using Amazon Web Services (AWS) we thought about how we could bring this service directly to their platform without relying on third-party cloud services. The basic idea was to isolate the part from Next.js core responsible for the image optimization, pack it into a serverless function (AWS Lambda) and use it as an external loader.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21xey6ao2by6dloez5ut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21xey6ao2by6dloez5ut.png" alt="Architecture diagram of the Terraform module"&gt;&lt;/a&gt;&lt;/p&gt;

Serverless AWS stack for image optimization with Next.js






&lt;p&gt;This way we would be able to achieve full support of all &lt;code&gt;next/image&lt;/code&gt; features and a maximum of scalability because AWS Lambda can scale up and down dynamically to met the demand.&lt;br&gt;
Since we do not want to run the serverless function on every request we put it behind a CloudFront distribution that is able to serve recurring requests for the same resources from cache. It also makes the whole thing noticeably faster since CloudFront CDN network is able to store the cache close the viewers location.&lt;/p&gt;

&lt;p&gt;Because we are already manage most of our DevOps stack with &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, we also used it to create the image optimizer. This also makes it possible to share the whole module so that it can easily be reused.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy the serverless image optimizer to AWS
&lt;/h2&gt;

&lt;p&gt;All you need to create your own image optimizer in 5 minutes is an AWS account and Terraform installed.&lt;/p&gt;

&lt;p&gt;Then create a new file called &lt;code&gt;main.tf&lt;/code&gt; (Can be added to the same directory as your Next.js app), which contains the definition and configuration of the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Main AWS region where the resources should be created in&lt;/span&gt;
&lt;span class="c1"&gt;# Should be close to where your Next.js deployment is located&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"next_image_optimizer"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"milliHQ/next-js-image-optimization/aws"&lt;/span&gt;

  &lt;span class="nx"&gt;next_image_domains&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sub.example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"domain"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next_image_optimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudfront_domain_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;next_image_domains&lt;/code&gt; variable should contain the domains where the external images should be fetched from. It takes the same configuration as the &lt;a href="https://nextjs.org/docs/basic-features/image-optimization#domains" rel="noopener noreferrer"&gt;Domains setting&lt;/a&gt; from Next.js.&lt;/p&gt;

&lt;p&gt;After that you are all set to deploy the module to your AWS account. All you need to do is running the two Terraform commands:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After Terraform has finished deploying the module, it shows you the following output on the Terminal:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

Outputs:

domain &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;distribution-id&amp;gt;.cloudfront.net"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The domain of the CloudFront distribution (&lt;code&gt;"&amp;lt;distribution-id.cloudfront.net"&lt;/code&gt;) is all you need to tell your Next.js app to use the newly created image optimizer. Open or create your &lt;code&gt;next.config.js&lt;/code&gt; and add the following lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;&lt;span class="gi"&gt;+  images: {
+    path: 'https://&amp;lt;distribution-id&amp;gt;.cloudfront.net/_next/image'
+  },
&lt;/span&gt;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Last step is now to redeploy your Next.js app with the changed configuration. Now you are using your own, self-hosted image optimizer.&lt;/p&gt;



&lt;p&gt;You can take look at the source code of the module on GitHub, since everything is Open Source:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/milliHQ" rel="noopener noreferrer"&gt;
        milliHQ
      &lt;/a&gt; / &lt;a href="https://github.com/milliHQ/terraform-aws-next-js-image-optimization" rel="noopener noreferrer"&gt;
        terraform-aws-next-js-image-optimization
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A drop-in image optimization loader for Next.js image component powered by AWS Lambda.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Terraform Next.js Image Optimization module for AWS&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/milliHQ/terraform-aws-next-js-image-optimization/actions/workflows/CI.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/milliHQ/terraform-aws-next-js-image-optimization/actions/workflows/CI.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A drop-in &lt;a href="https://nextjs.org/docs/basic-features/image-optimization#loader" rel="nofollow noopener noreferrer"&gt;image optimization loader&lt;/a&gt; for the Next.js image component &lt;code&gt;next/image&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; If you look for a complete solution to host a Next.js application with Terraform on AWS, please check out our &lt;a href="https://registry.terraform.io/modules/milliHQ/next-js/aws" rel="nofollow noopener noreferrer"&gt;Terraform Next.js module for AWS&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;✅  Terraform &lt;code&gt;v0.13+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅  Serverless image processing powered by &lt;a href="https://aws.amazon.com/lambda/" rel="nofollow noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅  Powerful optimization using the &lt;a href="https://github.com/lovell/sharp" rel="noopener noreferrer"&gt;sharp&lt;/a&gt; processing library&lt;/li&gt;
&lt;li&gt;✅  Performant image caching powered by &lt;a href="https://aws.amazon.com/cloudfront/" rel="nofollow noopener noreferrer"&gt;Amazon CloudFront&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅  Two-layer caching with &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/origin-shield.html" rel="nofollow noopener noreferrer"&gt;CloudFront Origin Shield&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅  Support for custom &lt;a href="https://nextjs.org/docs/basic-features/image-optimization#device-sizes" rel="nofollow noopener noreferrer"&gt;Device Sizes&lt;/a&gt; &amp;amp; &lt;a href="https://nextjs.org/docs/basic-features/image-optimization#image-sizes" rel="nofollow noopener noreferrer"&gt;Image Sizes&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Architecture&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The image optimization module is designed as a full stack AWS app.
It relies on multiple AWS services and connects them to work as a single application:&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/milliHQ/terraform-aws-next-js-image-optimization/blob/main/docs/assets/architecture.png?raw=true"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FmilliHQ%2Fterraform-aws-next-js-image-optimization%2Fraw%2Fmain%2Fdocs%2Fassets%2Farchitecture.png%3Fraw%3Dtrue" alt="Architecture overview diagram"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Deploy the module to AWS&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Initialize the module by creating a &lt;code&gt;main.tf&lt;/code&gt; file with the following content (you can place the file in the same directory where your Next.js project…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/milliHQ/terraform-aws-next-js-image-optimization" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  Looking for a full stack solution to deploy Next.js on AWS?
&lt;/h2&gt;

&lt;p&gt;If you don't want to mess around with configuration and want a complete solution to host Next.js serverless on AWS, please checkout our &lt;a href="https://github.com/milliHQ/terraform-aws-next-js" rel="noopener noreferrer"&gt;Terraform Next.js module for AWS&lt;/a&gt;. It has image optimization already built in and can handle most features from Next.js in a cost-efficient way.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>terraform</category>
      <category>devops</category>
      <category>aws</category>
    </item>
    <item>
      <title>Why we self-host our serverless Next.js site on AWS with Terraform</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Sat, 09 Jan 2021 15:46:39 +0000</pubDate>
      <link>https://dev.to/ofhouse/why-we-self-host-our-serverless-next-js-site-on-aws-with-terraform-4j70</link>
      <guid>https://dev.to/ofhouse/why-we-self-host-our-serverless-next-js-site-on-aws-with-terraform-4j70</guid>
      <description>&lt;p&gt;Last summer we had the idea to publish our Next.js site serverless on Amazon Web Services (AWS). Because we live in Germany and Vercel only &lt;a href="https://vercel.com/docs/edge-network/regions" rel="noopener noreferrer"&gt;supports&lt;/a&gt; a few of AWS regions for deploying your site in Europe (closest to Germany is Paris/France) we began a search how we could deploy our website to the AWS Frankfurt region.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We didn't want to rely on a cloud service (Serverless Components or Vercel) for deploying our application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploying our app should require nearly zero-config and support all major Next.js features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We created an Open Source Terraform module for doing it: &lt;a href="https://github.com/milliHQ/terraform-aws-next-js" rel="noopener noreferrer"&gt;https://github.com/milliHQ/terraform-aws-next-js&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How-to tutorial at the end of the article: Deploy Next.js to AWS&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;p&gt;By the time there were only a few choices for doing that and the most promising of them was the &lt;a href="https://github.com/serverless-nextjs/serverless-next.js" rel="noopener noreferrer"&gt;serverless-next.js&lt;/a&gt; project that is based on the Serverless Framework. By this time Serverless announced a new product called &lt;a href="https://www.serverless.com/blog/serverless-components-ga" rel="noopener noreferrer"&gt;Serverless Components&lt;/a&gt;. While it was introduced as a simpler way to deploy applications based on cloud functions, they also changed the way the deployment works: Instead of bundling and uploading it directly from your machine, you now had to use their cloud service, upload the source there and the Serverless cloud would then build and provision your app on AWS.&lt;/p&gt;

&lt;p&gt;While this concept isn’t something really new (Vercel does it the same way) we kept searching for a another way without relying on cloud services to build our app and risk a vendor lock-in.&lt;/p&gt;

&lt;p&gt;That time we also began using Terraform to manage our existing resources on AWS (Like domains, Static files on S3 buckets, CloudFront distributions, etc.). &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; is an Open-Source tool by HashiCorp for creating a code-based abstraction of your cloud resources and services (Similar to &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt; but not limited to AWS, since their open provider concept allows to manage resources even across multiple cloud providers).&lt;/p&gt;

&lt;h2&gt;
  
  
  Becoming independent
&lt;/h2&gt;

&lt;p&gt;While transitioning some of our infrastructure to Terraform we draw a few diagrams of our existing AWS services to define how they are connected with each other (e.g. which CloudFront distribution should be served from which S3 bucket). This is a really powerful concept in Terraform: You don't just say which resources and services you want to create, you can also connect them with code-based rules.&lt;/p&gt;

&lt;p&gt;At one time we drew a diagram of how a serverless architecture of our site with pure AWS services would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fesmabopu0ifdfnhx72vm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fesmabopu0ifdfnhx72vm.png" alt="The architecture of our serverless Next.js deployment"&gt;&lt;/a&gt;&lt;br&gt;The architecture of our serverless Next.js deployment
  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The idea was simple: Zero config in our Next.js app and let Terraform do the heavy lifting deploying our site to AWS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While it is relatively easy to do static hosting with Next.js on a S3 bucket and a CloudFront distribution, it is a bit more difficult to include server-side rendering (SSR) in this stack. &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;CloudFront&lt;/a&gt; is the CDN service from AWS that allows you to distribute static content like HTML, JavaScript or images in edge locations near your visitors so that following requests can be cached without hitting the origin resources. This way a site can be served with pretty low latency to customers around the globe while only paying a fraction of the costs that a request to the origin resource would cost.&lt;/p&gt;

&lt;p&gt;While CloudFront is good for reducing the load time of the site, it also offers a service called Lambda@Edge that allows us to run server-side code in these edge locations.&lt;br&gt;
This is a major key where our architecture is different from the &lt;a href="https://github.com/serverless-nextjs/serverless-next.js" rel="noopener noreferrer"&gt;serverless-next.js&lt;/a&gt; component: Serverless uses these Lambda@Edge functions to generate the SSR content while we only use it to determine from which origin the content should be served.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Next.js routing
&lt;/h2&gt;

&lt;p&gt;Next.js has a built-in router called &lt;a href="https://nextjs.org/docs/api-reference/next/router" rel="noopener noreferrer"&gt;next/router&lt;/a&gt;. The router is designed to work both on the client and the server side. In local development they work pretty similar on each side but when exporting the site to a serverless architecture the app gets splitted into different parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Static assets&lt;/strong&gt; (e.g. JavaScript, CSS, Images)&lt;/p&gt;

&lt;p&gt;These are files that have a hashed filename (like &lt;code&gt;some.chunk.14f293g29.js&lt;/code&gt;) and are not modified over time. Instead they get a new name when a new deployment is made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Static routes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Static routes are the ones that do not use parameters in their path (e.g. &lt;code&gt;example.com/about&lt;/code&gt;) and do not call &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering" rel="noopener noreferrer"&gt;&lt;code&gt;getServerSideProps()&lt;/code&gt;&lt;/a&gt;. These routes are pre-rendered at build time and packaged as static HTML files. So these routes can only be modified by a new deployment of the site.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dynamic routes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dynamic routes in Next.js need a server to render the corresponding HTML. That includes pages that are dynamically rendered (e.g. with data from a database) or use a dynamic parameter in their path (&lt;code&gt;example.com/blog/[postId]&lt;/code&gt;) where the final paths could not be calculated at build time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the static routes should not trigger a server (in our case the AWS Lambda function), we need to find a way to split the static and dynamic traffic before it hits the server.&lt;/p&gt;

&lt;p&gt;This is very simple for static assets, since all of their paths begin with &lt;code&gt;/_next/static/...&lt;/code&gt; so we can create a simple &lt;a href="https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CacheBehavior.html" rel="noopener noreferrer"&gt;CacheBehavior&lt;/a&gt; in CloudFront that looks for this prefix and redirect all requests for them to a S3 bucket where we uploaded our assets.&lt;/p&gt;

&lt;p&gt;For static and dynamic routes this is more complicated: Since a request for a static site looks exactly the same as for a dynamic route (e.g. &lt;code&gt;example.com/about&lt;/code&gt;, &lt;code&gt;example.com/blog/123&lt;/code&gt;) we need a custom logic that can decide if the route is static or whether it should be server-rendered by a Lambda.&lt;/p&gt;

&lt;p&gt;To accomplish this task we created a custom component called Proxy which is simply a serverless Node.js function that is able to determine where a route should be served from. Since this proxy is a crucial part for the performance of the site it is deployed directly at the CloudFront edge locations with Lambda@Edge.&lt;/p&gt;

&lt;p&gt;This is a trade-off we make here because running code in Lambda@Edge is three times more expensive than running the code in regional Lambdas. So we paid attention that the code for the proxy has as few dependencies as possible and can be executed really fast once the Lambda is warmed up.&lt;/p&gt;

&lt;p&gt;We built the proxy logic so that it behaves in the same way as Vercel does with their &lt;a href="https://vercel.com/docs/configuration#routes" rel="noopener noreferrer"&gt;routing configuration&lt;/a&gt;. This way we ensured a maximum of compatibility with the way Vercel does deployments and were able to recreate features for Next.js that they offer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing Next.js for a serverless environment
&lt;/h2&gt;

&lt;p&gt;It seems strange at first that we need that much effort to make Next.js serverless because seeing it running on Vercel so smoothly, you may think it is a native serverless application.&lt;/p&gt;

&lt;p&gt;That’s wrong. Next.js at its core is simply a Node.js webserver and even on Vercel it runs as a server rather than a serverless function. The trick here is to convert the invoke event from the cloud function to a HTTP server request and then parse the HTTP server response back to a callback parameter of the function:&lt;/p&gt;


  &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzj44yd0a5vuuykfmihw6.png" alt="Routing flow of a request sent to a serverless Next.js function"&gt;The flow of a request to a serverless Next.js site
  


&lt;p&gt;In AWS Lambda with JavaScript you can simply achieve this by starting a local HTTP server for each invocation and then create a client request to get the server response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Reference implementation how to start a Next.js webserver inside a Lambda&lt;/span&gt;
&lt;span class="c1"&gt;// from API Gateway invocation&lt;/span&gt;
&lt;span class="c1"&gt;// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Import the generated Next.js server from somewhere&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NextServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Deconstruct API Gateway event&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Start local server and get address once it's running&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;listeningCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;respBodyChunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;respBodyChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bodyBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;respBodyChunks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bodyBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="na"&gt;isBase64Encoded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Over time there emerged &lt;a href="https://github.com/netlify/next-on-netlify" rel="noopener noreferrer"&gt;multiple&lt;/a&gt; &lt;a href="https://github.com/vincent-herlemont/next-aws-lambda-webpack-plugin" rel="noopener noreferrer"&gt;solutions&lt;/a&gt; for doing this but all of them require a lot of additional configuration to make it work. Since our goal was to require as little configuration as possible, we took a closer look how Vercel achieved this.&lt;/p&gt;

&lt;p&gt;What makes Vercel a really outstanding company is that they open sourced a lot of their internal tooling. They use a package called &lt;a href="https://www.npmjs.com/package/@vercel/next" rel="noopener noreferrer"&gt;&lt;code&gt;now-next&lt;/code&gt;&lt;/a&gt; that does the packaging of Next.js before it gets deployed to their infrastructure (The name “now” comes from the time the company &lt;a href="https://vercel.com/blog/zeit-is-now-vercel" rel="noopener noreferrer"&gt;was named Zeit&lt;/a&gt;, which means “Time” in German, which was a pretty great naming in my opinion, since I am German too 😊).&lt;/p&gt;

&lt;p&gt;Because the routing logic of our proxy component reproduces the behavior of the Vercel proxy it was now pretty easy for us to make the output from their builder to work with our architecture.&lt;/p&gt;

&lt;p&gt;This way we achieved a similar developer experience that Vercel has while running the whole build and deployment process on our own machine.&lt;/p&gt;

&lt;p&gt;&lt;a id="deploy-next-js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploy Next.js to AWS
&lt;/h2&gt;

&lt;p&gt;Okay, we talked a lot about why and how we built the architecture the way it is, now it is time to show you the whole thing. Because we profited a lot of Open Source in this project and in the past, we decided to make the module publicly available too:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/milliHQ" rel="noopener noreferrer"&gt;
        milliHQ
      &lt;/a&gt; / &lt;a href="https://github.com/milliHQ/terraform-aws-next-js" rel="noopener noreferrer"&gt;
        terraform-aws-next-js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform module for building and deploying Next.js apps to AWS. Supports SSR (Lambda), Static (S3) and API (Lambda) pages.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    &lt;strong&gt;Note&lt;/strong&gt;
    &lt;br&gt;
    The main branch currently contains the atomic deployments alpha preview
    &lt;br&gt;
    For the lastest stable release, check out the &lt;a href="https://github.com/milliHQ/terraform-aws-next-js/tree/v0.x" rel="noopener noreferrer"&gt;&lt;code&gt;v0.x&lt;/code&gt; branch&lt;/a&gt;
  &lt;/p&gt;
  &lt;p&gt;
    Please see our blog post &lt;a href="https://milli.is/blog/the-road-to-atomic-deployments" rel="nofollow noopener noreferrer"&gt;"The road to Atomic Deployments"&lt;/a&gt;&lt;br&gt;or watch the latest release review for more information
    &lt;br&gt;
    &lt;br&gt;
    &lt;a href="https://www.youtube.com/watch?v=NY3zKnIcLd4" rel="nofollow noopener noreferrer"&gt;
      &lt;img src="https://camo.githubusercontent.com/9ba2f02b3cd611e4050f415ed89b9799c5e4b1d5ab905c10012b0d5a5451893a/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f4e59337a4b6e49634c64342f302e6a7067" height="260px"&gt;
    &lt;/a&gt;
  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Terraform Next.js module for AWS&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/milliHQ/terraform-aws-next-js/workflows/CI/badge.svg"&gt;&lt;img src="https://github.com/milliHQ/terraform-aws-next-js/workflows/CI/badge.svg" alt="CI status"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A zero-config Terraform module for self-hosting Next.js sites serverless on AWS Lambda.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Some features are still under development, here is a list of features that are currently supported and what we plan to bring with the next releases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅  Supports any version of &lt;a href="https://nextjs.org/" rel="nofollow noopener noreferrer"&gt;Next.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅  &lt;a href="https://www.terraform.io/" rel="nofollow noopener noreferrer"&gt;Terraform&lt;/a&gt; &lt;code&gt;v0.15+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅  Unlimited parallel deployments of Next.js apps (atomic deployments)&lt;/li&gt;
&lt;li&gt;✅  Static, SSG, Lambda and API pages (with &lt;a href="https://nextjs.org/docs/routing/dynamic-routes" rel="nofollow noopener noreferrer"&gt;dynamic routes&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;✅  Automatic expiration of old static assets&lt;/li&gt;
&lt;li&gt;✅  &lt;a href="https://nextjs.org/docs/api-reference/next.config.js/rewrites" rel="nofollow noopener noreferrer"&gt;Rewrites&lt;/a&gt; &amp;amp; &lt;a href="https://nextjs.org/docs/api-reference/next.config.js/redirects" rel="nofollow noopener noreferrer"&gt;Redirects&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅  &lt;a href="https://nextjs.org/docs/basic-features/image-optimization" rel="nofollow noopener noreferrer"&gt;Image Component &amp;amp; Image Optimization&lt;/a&gt; support&lt;/li&gt;
&lt;li&gt;🚧  &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration" rel="nofollow noopener noreferrer"&gt;Incremental Static Regeneration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⛔️  &lt;a href="https://nextjs.org/docs/advanced-features/middleware" rel="nofollow noopener noreferrer"&gt;Middleware&lt;/a&gt; (Not supported by Lambda@Edge / CloudFront functions)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Architecture&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;The Next.js Terraform module…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/milliHQ/terraform-aws-next-js" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Installation and deployment can be broken down to a few easy steps, so if you have a minute left to spend, here is how you can deploy your own Next.js app to AWS:&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Make sure you have &lt;a href="https://www.terraform.io/downloads.html" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create &lt;a href="https://docs.aws.amazon.com/powershell/latest/userguide/pstools-appendix-sign-up.html" rel="noopener noreferrer"&gt;AWS Access Keys&lt;/a&gt; (&lt;code&gt;Access Key ID&lt;/code&gt; &amp;amp; &lt;code&gt;Secret Access Key&lt;/code&gt;) from an AWS Account with admin privileges.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new file in the root directory of your Next.js project called &lt;code&gt;main.tf&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Main region where the resources should be created in&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"tf_next"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"milliHQ/next-js/aws"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"cloudfront_domain_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tf_next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudfront_domain_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the Next.js app
&lt;/h3&gt;

&lt;p&gt;In the directory of your Next.js app run the bundler (don't worry if you have a custom next.config.js we got you covered):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tf-next build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy the Next.js app
&lt;/h3&gt;

&lt;p&gt;In the same directory now run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make the AWS credentials available for Terraform&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-access-key&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-secret-access-key&amp;gt;

&lt;span class="c"&gt;# Initialize Terraform&lt;/span&gt;
terraform init
&lt;span class="c"&gt;# Deploy your app&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After Terraform successfully created the deployment, it you should show you the following output on your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Apply complete!
&amp;gt;
&amp;gt; Outputs:
&amp;gt;
&amp;gt; cloudfront_domain_name = "xxx.cloudfront.net"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you visit the &lt;code&gt;https://xxx.cloudfront.net&lt;/code&gt; domain in your browser you should now be able to see your application deployed serverless on AWS!&lt;/p&gt;

&lt;p&gt;Even if your app just don't need server-side rendering (SSR), our setup works for this too without any extra config.&lt;/p&gt;




&lt;p&gt;We have made some example applications to try out on our GitHub repo to checkout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/milliHQ/terraform-aws-next-js/tree/main/examples/complete" rel="noopener noreferrer"&gt;&lt;strong&gt;Complete example&lt;/strong&gt;&lt;/a&gt; (with SSR, static routes, rewrites and redirects)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/milliHQ/terraform-aws-next-js/tree/main/examples/static" rel="noopener noreferrer"&gt;&lt;strong&gt;Static example&lt;/strong&gt;&lt;/a&gt; (no SSR, just static sites, still supports rewrites and redirects)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/milliHQ/terraform-aws-next-js/tree/main/examples/with-custom-domain" rel="noopener noreferrer"&gt;&lt;strong&gt;Custom domain&lt;/strong&gt;&lt;/a&gt; (In case you just don't want to host your site on a xxx.cloudfront.net subdomain)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading this far, if you have questions or feature requests, you can reach out on &lt;a href="https://twitter.com/milliHQ" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or &lt;a href="https://github.com/milliHQ" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have a great day!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>javascript</category>
      <category>serverless</category>
      <category>terraform</category>
    </item>
    <item>
      <title>How to unpack .wpress archive files created by the All-in-one-Wp-Migration Wordpress plugin</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Sat, 21 Nov 2020 20:35:47 +0000</pubDate>
      <link>https://dev.to/ofhouse/how-to-unpack-wpress-archive-files-created-by-the-all-in-one-wp-migration-wordpress-plugin-k63</link>
      <guid>https://dev.to/ofhouse/how-to-unpack-wpress-archive-files-created-by-the-all-in-one-wp-migration-wordpress-plugin-k63</guid>
      <description>&lt;p&gt;Recently I needed to download some files from a Wordpress installation where the client only gave me access to the admin dashboard. Fortunately the &lt;a href="https://wordpress.org/plugins/all-in-one-wp-migration/" rel="noopener noreferrer"&gt;All-in-One WP Migration&lt;/a&gt; plugin was already installed, so I could take a quick backup of the whole site by downloading the installed plugins, theme and database.&lt;/p&gt;

&lt;p&gt;To my surprise downloading the backup from the All-in-One WP Migration plugin only gave me a single compressed &lt;code&gt;migration.wpress&lt;/code&gt; file that any unpack tool refused to extract. A little web search brought me to a five year old tool called &lt;a href="https://github.com/fifthsegment/Wpress-Extractor" rel="noopener noreferrer"&gt;Wpress-Extractor&lt;/a&gt; but the provided binaries for MacOS refused to work because the package was already too old.&lt;/p&gt;

&lt;p&gt;So I decided to rewrite this little helpful tool in Node.js to make it cross-platform compatible for Windows, MacOS and Linux.&lt;/p&gt;




&lt;p&gt;Ok here it is: A simple 2-step tutorial how to extract a file with the &lt;code&gt;.wpress&lt;/code&gt; extension on your computer:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Step&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Make sure that you have &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed in your computer:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# =&amp;gt; v14.12.0&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2. Step&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In your terminal navigate to the location where the &lt;code&gt;.wpress&lt;/code&gt; file is downloaded (e.g. &lt;code&gt;~/Downloads&lt;/code&gt;) and run the following command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx wpress-extract migration.wpress


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;It then creates a new folder &lt;code&gt;migration/&lt;/code&gt; where the content is extracted into.&lt;/p&gt;



&lt;p&gt;That's it for today. If you would like to take a deeper look at this tool, the source code is open source, so feel free to visit the GitHub repository:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ofhouse" rel="noopener noreferrer"&gt;
        ofhouse
      &lt;/a&gt; / &lt;a href="https://github.com/ofhouse/wpress-extract" rel="noopener noreferrer"&gt;
        wpress-extract
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A simple Node.js tool for extracting wpress archive files generated by the All-in-one-Wp-Migration Wordpress plugin.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>wordpress</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Enhance your Stencil Web Components in Storybook with Knobs, Actions and JSX</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Wed, 04 Mar 2020 15:17:31 +0000</pubDate>
      <link>https://dev.to/ofhouse/enhance-your-stencil-web-components-in-storybook-with-knobs-actions-and-jsx-54m4</link>
      <guid>https://dev.to/ofhouse/enhance-your-stencil-web-components-in-storybook-with-knobs-actions-and-jsx-54m4</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/ofhouse/build-a-web-component-library-with-stencil-and-storybook-c27"&gt;previous part&lt;/a&gt; of this series we got our setup of &lt;a href="https://stenciljs.com/"&gt;Stencil&lt;/a&gt; and &lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt; up and running. This part goes a little deeper and we'll learn how to improve our stories by introducing Storybook's Knobs and Actions addons to our existing project.&lt;br&gt;
Finally we'll learn how to simplify our stories by writing our Stencil Web Components directly in JSX rather than in plain JavaScript.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are here to learn how to write your stories in JSX, you can skip to the end of this article: Use JSX to render your components in Storybook.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Adding Knobs and Action addons to the stack
&lt;/h1&gt;

&lt;p&gt;To make a greater use of Storybook we first add the &lt;a href="https://www.npmjs.com/package/@storybook/addon-knobs"&gt;Knobs&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@storybook/addon-actions"&gt;Actions&lt;/a&gt; addons to our existing setup. The Knobs addon provides us a simple interface to modify the properties of our Web Components on the fly. The Actions addon logs all events from our Web Component, so that we can see which events are emitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vjmCyiGh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/opyprn2zq988lv8ihw3k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vjmCyiGh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/opyprn2zq988lv8ihw3k.gif" alt="Screen-Recording of Storybook with knobs and actions addons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First we install the addons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @storybook/addon-knobs @storybook/addon-actions
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And then add them to our storybook configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; .storybook/main.js

...
&lt;span class="p"&gt;module.exports = {
&lt;/span&gt;  stories: ['../src/**/*.stories.js'],
  addons: [
    '@storybook/addon-notes/register',
&lt;span class="gi"&gt;+   '@storybook/addon-knobs/register',
+   '@storybook/addon-actions/register',
&lt;/span&gt;  ],
  ...
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Add a new component
&lt;/h1&gt;

&lt;p&gt;To learn how to integrate the Storybook addons with a Web Component we start by creating a new button component called &lt;code&gt;&amp;lt;my-button&amp;gt;&lt;/code&gt;. Again we use Stencil's built-in CLI here for creating a boilerplate for our new component:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @stencil/core generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gyG_u7Q4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0ytkuu42bor7fkdgdkcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gyG_u7Q4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0ytkuu42bor7fkdgdkcq.png" alt="Screenshot of the Stencil CLI for creating a new component - part 1"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RO_UTdsW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6e61rzlrmdk6o91vqok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RO_UTdsW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6e61rzlrmdk6o91vqok.png" alt="Screenshot of the Stencil CLI for creating a new component - part 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the CLI has finished creating the new files for our component, make sure to run a quick build so that Stencil also adds a simple readme to this component:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since our new button component is pretty empty right now we give it more content by replacing it with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-button.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@stencil/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-button.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyButton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UIEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Our component now has a property &lt;code&gt;label&lt;/code&gt; where we can define what text is shown on our button and an &lt;code&gt;onClick&lt;/code&gt; event which is fired when the button is clicked.&lt;br&gt;
Now we are ready to add a new story for this button by creating a new file called &lt;code&gt;my-button.stories.js&lt;/code&gt; in the same directory:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;src/
&lt;/span&gt;&lt;span class="err"&gt;└──&lt;/span&gt; components/
    ├── my-component/
    └── my-button/
        ├── my-button.css
&lt;span class="gi"&gt;+       ├── my-button.stories.js
&lt;/span&gt;        ├── my-button.tsx
        └── readme.md
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Inside of the file we now define our story:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-button.stories.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storiesOf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withKnobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/addon-knobs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/addon-actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./readme.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;withActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onClick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;withKnobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Knob property Label&lt;/span&gt;
      &lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Me!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Event handler for onClick&lt;/span&gt;
      &lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buttonElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When we now start storybook we can now see that a new panel with our addons is added to the view:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Y-Va64X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jf7yzsza1fz289csdbep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Y-Va64X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jf7yzsza1fz289csdbep.png" alt="Screenshot of Storybook with the addon panel open"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Use JSX to render your Web Components in Storybook &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Maybe you are wondering why we wrote the implementation of our Stencil components in JSX while our stories use plain JavaScript to initialize our components. Let's change that.&lt;br&gt;
I saw the following tweet by &lt;a href="https://twitter.com/DasSurma/status/1232742089408073728"&gt;Surma&lt;/a&gt; last week where he shows that with a little help of a babel transformer and 15 lines of code it is pretty easy to write DOM in JSX:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WlL5stXz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/ERuTnOLXYAgSEIE.png" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--09nIvU9o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1216718697571790849/faqjS4QM_normal.jpg" alt="Surma profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Surma
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @dassurma
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Wait, is this news to anyone? Am I just late to the party? Am I missing something?&lt;br&gt;&lt;br&gt;With this 15 line `h` function, you can use JSX for DOM ops w/ all the good stuff:&lt;br&gt;&lt;br&gt;- Inline event handlers&lt;br&gt;- Custom elements&lt;br&gt;- Props and attributes (which React still can’t do, lol)&lt;br&gt;&lt;br&gt;I dig it 🤨 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      18:59 PM - 26 Feb 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1232742089408073728" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1232742089408073728" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      66
      &lt;a href="https://twitter.com/intent/like?tweet_id=1232742089408073728" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      482
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;There is already a tiny library out there called &lt;a href="https://www.npmjs.com/package/jsx-dom"&gt;&lt;code&gt;jsx-dom&lt;/code&gt;&lt;/a&gt; so we don't have to copy the code from the screenshot.&lt;/p&gt;

&lt;p&gt;In order to make it work, we need to add 3 new packages to our setup:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @babel/plugin-syntax-jsx @babel/plugin-transform-react-jsx jsx-dom
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then we add a &lt;code&gt;.babelrc&lt;/code&gt; file to the root of our project:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .babelrc&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plugins&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/plugin-syntax-jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@babel/plugin-transform-react-jsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pragma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now we are ready to rewrite the story for our button component in JSX. Simply make sure to add the pragma &lt;code&gt;import { h } from 'jsx-dom';&lt;/code&gt; to the top of every story that uses JSX. We then can bind the properties to variables or inline them directly:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-button.stories.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;storiesOf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withKnobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/addon-knobs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/addon-actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsx-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./readme.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;storiesOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;withActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onClick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;withKnobs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;my&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading. There are still some features which are missing in this setup (e.g. Hot-Module-Replacement) which I hopefully can figure out in the coming weeks. So feel free to follow me here for more updates on this topic!&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;A live-demo of the Storybook is available here: &lt;a href="https://ofhouse.github.io/storybook-stencil-example"&gt;https://ofhouse.github.io/storybook-stencil-example&lt;/a&gt;&lt;br&gt;
If your are interested to try it out by yourself, you can check out the full repository on GitHub:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ofhouse"&gt;
        ofhouse
      &lt;/a&gt; / &lt;a href="https://github.com/ofhouse/storybook-stencil-example"&gt;
        storybook-stencil-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Example project which uses Stencil components in Storybook. 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://camo.githubusercontent.com/7fcd5327fa1f386e589bb1cceb41d12f6181a957/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d4275696c74253230576974682532305374656e63696c2d3136313631642e7376673f6c6f676f3d64617461253341696d616765253246737667253242786d6c2533426261736536342532435044393462577767646d567963326c76626a30694d5334774969426c626d4e765a476c755a7a30696458526d4c546769507a344b504345744c5342485a57356c636d4630623349364945466b62324a6c49456c736248567a64484a6864473979494445354c6a49754d53776755315a484945563463473979644342516248566e4c556c754943346755315a4849465a6c636e4e7062323436494459754d444167516e5670624751674d436b674943307450676f3863335a6e49485a6c636e4e7062323439496a45754d53496761575139496b786865575679587a4569494868746247357a50534a6f644852774f693876643364334c6e637a4c6d39795a7938794d4441774c334e325a79496765473173626e4d3665477870626d7339496d6830644841364c79393364336375647a4d7562334a6e4c7a45354f546b7665477870626d736949486739496a427765434967655430694d48423449676f4a49485a705a58644362336739496a41674d4341314d5449674e5445794969427a64486c735a5430695a573568596d786c4c574a685932746e636d3931626d5136626d5633494441674d4341314d5449674e5445794f794967654731734f6e4e7759574e6c50534a77636d567a5a584a325a5349253242436a787a64486c735a5342306558426c50534a305a5868304c324e7a63794925324243676b756333517765325a706247773649305a47526b5a47526a7439436a777663335235624755253242436a78775958526f49474e7359584e7a50534a7a6444416949475139496b30304d6a51754e79777a4e7a4d754f574d774c444d334c6a59744e5455754d5377324f4334324c546b794c6a63734e6a67754e6b67784f4441754e474d744d7a63754f5377774c546b794c6a63744d7a41754e7930354d6934334c5459344c6a5a324c544d754e6d677a4d7a59754f56597a4e7a4d754f586f694c7a344b50484268644767675932786863334d39496e4e304d4349675a443069545451794e4334334c4449354d693478534445344d4334305979307a4e7934324c4441744f5449754e79307a4d5330354d6934334c5459344c6a5a324c544d754e6b677a4d7a4a6a4d7a63754e6977774c446b794c6a63734d7a45734f5449754e7977324f433432566a49354d6934786569497650676f38634746306143426a6247467a637a3069633351774969426b50534a4e4e4449304c6a63734d5451784c6a64494f4463754e3359744d793432597a41744d7a63754e6977314e4334344c5459344c6a59734f5449754e7930324f43343253444d7a4d6d4d7a4e7934354c4441734f5449754e79777a4d4334334c446b794c6a63734e6a67754e6c59784e4445754e336f694c7a344b5043397a646d63253242436725334425334426636f6c6f72413d313631363164267374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/7fcd5327fa1f386e589bb1cceb41d12f6181a957/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d4275696c74253230576974682532305374656e63696c2d3136313631642e7376673f6c6f676f3d64617461253341696d616765253246737667253242786d6c2533426261736536342532435044393462577767646d567963326c76626a30694d5334774969426c626d4e765a476c755a7a30696458526d4c546769507a344b504345744c5342485a57356c636d4630623349364945466b62324a6c49456c736248567a64484a6864473979494445354c6a49754d53776755315a484945563463473979644342516248566e4c556c754943346755315a4849465a6c636e4e7062323436494459754d444167516e5670624751674d436b674943307450676f3863335a6e49485a6c636e4e7062323439496a45754d53496761575139496b786865575679587a4569494868746247357a50534a6f644852774f693876643364334c6e637a4c6d39795a7938794d4441774c334e325a79496765473173626e4d3665477870626d7339496d6830644841364c79393364336375647a4d7562334a6e4c7a45354f546b7665477870626d736949486739496a427765434967655430694d48423449676f4a49485a705a58644362336739496a41674d4341314d5449674e5445794969427a64486c735a5430695a573568596d786c4c574a685932746e636d3931626d5136626d5633494441674d4341314d5449674e5445794f794967654731734f6e4e7759574e6c50534a77636d567a5a584a325a5349253242436a787a64486c735a5342306558426c50534a305a5868304c324e7a63794925324243676b756333517765325a706247773649305a47526b5a47526a7439436a777663335235624755253242436a78775958526f49474e7359584e7a50534a7a6444416949475139496b30304d6a51754e79777a4e7a4d754f574d774c444d334c6a59744e5455754d5377324f4334324c546b794c6a63734e6a67754e6b67784f4441754e474d744d7a63754f5377774c546b794c6a63744d7a41754e7930354d6934334c5459344c6a5a324c544d754e6d677a4d7a59754f56597a4e7a4d754f586f694c7a344b50484268644767675932786863334d39496e4e304d4349675a443069545451794e4334334c4449354d693478534445344d4334305979307a4e7934324c4441744f5449754e79307a4d5330354d6934334c5459344c6a5a324c544d754e6b677a4d7a4a6a4d7a63754e6977774c446b794c6a63734d7a45734f5449754e7977324f433432566a49354d6934786569497650676f38634746306143426a6247467a637a3069633351774969426b50534a4e4e4449304c6a63734d5451784c6a64494f4463754e3359744d793432597a41744d7a63754e6977314e4334344c5459344c6a59734f5449754e7930324f43343253444d7a4d6d4d7a4e7934354c4441734f5449754e79777a4d4334334c446b794c6a63734e6a67754e6c59784e4445754e336f694c7a344b5043397a646d63253242436725334425334426636f6c6f72413d313631363164267374796c653d666c61742d737175617265" alt="Built With Stencil"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
Storybook Stencil Example&lt;/h1&gt;
&lt;p&gt;This is a starter project for building a Web Component library with &lt;a href="https://stenciljs.com/" rel="nofollow"&gt;Stencil&lt;/a&gt; and &lt;a href="https://storybook.js.org/" rel="nofollow"&gt;Storybook&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Getting Started&lt;/h2&gt;
&lt;p&gt;To start clone this repo into a new directory and run the following commands:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;npm install
npm start&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For a step-by-step tutorial how to recreate this from scratch you can follow this series of blog posts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://dev.to/ofhouse/build-a-web-component-library-with-stencil-and-storybook-c27" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/9eb2d9ca714e63de26875dd945c4a1382d23233c/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d36505a377a566d6f2d2d2f635f696d616767615f7363616c652c665f6175746f2c666c5f70726f67726573736976652c685f3530302c715f6175746f2c775f313030302f68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d587a73357137526e2d2d2f635f696d616767615f7363616c65253243665f6175746f253243666c5f70726f6772657373697665253243685f343230253243715f6175746f253243775f313030302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f692f6772786c63766362776b7a746e316833747365732e6a7067" height="240px;"&gt;&lt;br&gt;Part 1: &lt;b&gt;Build a Web Component library with Stencil and Storybook&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://dev.to/ofhouse/enhance-your-stencil-web-components-in-storybook-with-knobs-actions-and-jsx-54m4" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/b4e02bcc959028514ce8d91b26c8d1620d3bc9a3/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d58585f45555933582d2d2f635f696d616767615f7363616c652c665f6175746f2c666c5f70726f67726573736976652c685f3530302c715f6175746f2c775f313030302f68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d6c6d4e704c5737592d2d2f635f696d616767615f7363616c65253243665f6175746f253243666c5f70726f6772657373697665253243685f343230253243715f6175746f253243775f313030302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f692f653870656163726f71687a7039757639727374742e6a7067" height="240px;"&gt;&lt;br&gt;Part 2: &lt;b&gt;Enhance your Stencil Web Components in Storybook with Knobs, Actions and JSX&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storybook.js.org/docs/guides/guide-html/" rel="nofollow"&gt;Storybook for HTML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-notes" rel="nofollow"&gt;Storybook Addon Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-knobs" rel="nofollow"&gt;Storybook Addon Knobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-actions" rel="nofollow"&gt;Storybook Addon Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Author&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;a href="https://github.com/ofhouse"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IZJT1wAQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/472867%3Fv%3D4" width="100px;"&gt;&lt;br&gt;&lt;b&gt;Felix Haus&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://felix.house/" rel="nofollow"&gt;Website&lt;/a&gt; • &lt;a href="https://twitter.com/ofhouse" rel="nofollow"&gt;Twitter&lt;/a&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
License&lt;/h2&gt;
&lt;p&gt;MIT - see &lt;a href="https://raw.githubusercontent.com/ofhouse/storybook-stencil-example/master/readme.md/./LICENSE"&gt;LICENSE&lt;/a&gt; for details.&lt;/p&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ofhouse/storybook-stencil-example"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>stencil</category>
      <category>storybook</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build a Web Component library with Stencil and Storybook</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Tue, 18 Feb 2020 16:48:41 +0000</pubDate>
      <link>https://dev.to/ofhouse/build-a-web-component-library-with-stencil-and-storybook-c27</link>
      <guid>https://dev.to/ofhouse/build-a-web-component-library-with-stencil-and-storybook-c27</guid>
      <description>&lt;p&gt;In this little tutorial we learn how to add &lt;a href="https://www.webcomponents.org/" rel="noopener noreferrer"&gt;Web Components&lt;/a&gt; created with Stencil to a Storybook setup. &lt;a href="https://stenciljs.com/" rel="noopener noreferrer"&gt;Stencil&lt;/a&gt; is a framework which let us write Web Components in a JSX-style syntax similar to &lt;a href="https://reactjs.org/docs/react-component.html" rel="noopener noreferrer"&gt;React's class-components&lt;/a&gt;. It then compiles it to native Web Components code to make it usable in the browser.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Create a new Stencil project
&lt;/h1&gt;

&lt;p&gt;First we initialize a new Stencil project from scratch with &lt;a href="https://www.npmjs.com/package/create-stencil" rel="noopener noreferrer"&gt;Stencil's handy CLI tool&lt;/a&gt;. It generates an initial project structure for us where we can simply add new components later on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you already have a Stencil project you can skip this step and go straight to Step 2.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init stencil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the following dialog Stencil asks us to pick a project type. Since we want to generate a library of Web Components choose the &lt;code&gt;component&lt;/code&gt; option here and continue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fue4zzfv0emqvlhnlwgqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fue4zzfv0emqvlhnlwgqa.png" alt="Screenshot of Stencils project wizard where the user can choose the type of the project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the last step of the project generator we choose a name for the project, for simplicity we name it &lt;code&gt;storybook-stencil-example&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkmioucv3ylnqgm9mge4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkmioucv3ylnqgm9mge4b.png" alt="Screenshot of Stencils project wizard where the user can choose the name of the project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we navigate into our newly created stencil project and install the dependencies:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;storybook-stencil-example  &lt;span class="c"&gt;# Navigate into project dir&lt;/span&gt;
npm i                         &lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  2. Add Storybook to the project &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;Because Stencil components are compiled to Web Components we simply can use &lt;a href="https://storybook.js.org/docs/guides/guide-html/" rel="noopener noreferrer"&gt;Storybook for HTML&lt;/a&gt; project type here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bootstrap storybook&lt;/span&gt;
npx &lt;span class="nt"&gt;-p&lt;/span&gt; @storybook/cli sb init &lt;span class="nt"&gt;--type&lt;/span&gt; html

&lt;span class="c"&gt;# Install additional dependencies for our setup&lt;/span&gt;
npm i &lt;span class="nt"&gt;--save-dev&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  write-file-webpack-plugin &lt;span class="se"&gt;\&lt;/span&gt;
  copy-webpack-plugin &lt;span class="se"&gt;\&lt;/span&gt;
  @storybook/addon-notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we need to make some changes to the default configuration from Storybook. We&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .storybook/main.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CopyPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WriteFilePlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;write-file-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OUTPUT_DIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Stencil names the project entry the same as the project&lt;/span&gt;
&lt;span class="c1"&gt;// Look for the file `dist/&amp;lt;your-project-name&amp;gt;.js` to find out what to insert here&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PROJECT_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;storybook-stencil-example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;stories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/**/*.stories.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;addons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@storybook/addon-notes/register&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// Custom webpack config to tell Storybook where to find the compiled files from Stencil&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;webpackFinal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PROJECT_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.js`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collection/components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;jsFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`collection/components/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.js`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsFilePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Add CSS&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cssFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;`collection/components/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.css`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssFilePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cssFilePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Add all static files to Storybook&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CopyPlugin&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;**/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Write the files to disk and not to memory&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WriteFilePlugin&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  3. Create our first story
&lt;/h1&gt;

&lt;p&gt;The Stencil project setup has already added a sample component for us named &lt;code&gt;my-component&lt;/code&gt;. So let's create our first story for this component. Inside the component folder add a new file &lt;code&gt;my-component.stories.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;src/
└── components/
    └── my-component/
        ├── my-component.css
        ├── my-component.e2e.ts
        ├── my-component.stories.js  &lt;span class="c"&gt;# &amp;lt;-- Add the file here&lt;/span&gt;
        ├── my-component.tsx
        └── readme.md
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Of course we could also write the stories in TypeScript because stencil already relies on it but to keep things simple we use plain JavaScript for now.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my-component.stories.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./readme.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;readme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;my-component first="Millie" middle="Bobby" last="Brown"&amp;gt;&amp;lt;/my-component&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Importing the auto-generated &lt;code&gt;readme.md&lt;/code&gt; from Stencil gives us a free documentation of our component which can also be showed in Storybooks "Notes" tab of the component.&lt;/p&gt;
&lt;h1&gt;
  
  
  4. Development workflow
&lt;/h1&gt;

&lt;p&gt;To make the best use of Stencil and Storybook, both support a live-reload development workflow so that we can see changes we make to our Stencil components directly in Storybook without reloading the browser.&lt;br&gt;
To make this work we start the Stencil dev server and the Storybook dev server parallel in two different terminals:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terminal 1 - Run Stencil compiler in watch mode&lt;/span&gt;
npm start

&lt;span class="c"&gt;# Terminal 2 - Run Storybook&lt;/span&gt;
npm run storybook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now the final result should now look like this in the browser. The canvas gives us a preview of our component while the note tab holds the documentation of the component itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ofhouse.github.io/storybook-stencil-example" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkkj5wjfhxrz5sesj8wsx.gif" alt="Screen recording of the final result where a Stencil component is loaded inside storybook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also see a live-demo of the Storybook here: &lt;a href="https://ofhouse.github.io/storybook-stencil-example/?path=/story/my-component--default" rel="noopener noreferrer"&gt;https://ofhouse.github.io/storybook-stencil-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;This is only a basic setup guide for Stencil and Storybook, to make use of Storybook's advanced features like &lt;a href="https://github.com/storybookjs/storybook/tree/master/addons/knobs" rel="noopener noreferrer"&gt;Knobs&lt;/a&gt; or &lt;a href="https://github.com/storybookjs/storybook/tree/master/addons/actions" rel="noopener noreferrer"&gt;Actions&lt;/a&gt; I will add a second tutorial shortly. So follow me for more content!&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;&lt;em&gt;This guide was made possible by the work of of &lt;a href="https://github.com/popcorn245" rel="noopener noreferrer"&gt;Bobby Johnson&lt;/a&gt;. He has made a really nice video of the whole process of his Stencil and Storybook setup here: &lt;a href="https://youtu.be/XwHtPw3izLE" rel="noopener noreferrer"&gt;Using StencilJS with Storybook on YouTube&lt;br&gt;
&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;You can find the whole example project from this tutorial in the following repository:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ofhouse" rel="noopener noreferrer"&gt;
        ofhouse
      &lt;/a&gt; / &lt;a href="https://github.com/ofhouse/storybook-stencil-example" rel="noopener noreferrer"&gt;
        storybook-stencil-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Example project which uses Stencil components in Storybook. 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/43bc52727967f19560c435cded58054c288d74d79a44bfc77f4623756ea56be4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d4275696c74253230576974682532305374656e63696c2d3136313631642e7376673f6c6f676f3d64617461253341696d616765253246737667253242786d6c2533426261736536342532435044393462577767646d567963326c76626a30694d5334774969426c626d4e765a476c755a7a30696458526d4c546769507a344b504345744c5342485a57356c636d4630623349364945466b62324a6c49456c736248567a64484a6864473979494445354c6a49754d53776755315a484945563463473979644342516248566e4c556c754943346755315a4849465a6c636e4e7062323436494459754d444167516e5670624751674d436b674943307450676f3863335a6e49485a6c636e4e7062323439496a45754d53496761575139496b786865575679587a4569494868746247357a50534a6f644852774f693876643364334c6e637a4c6d39795a7938794d4441774c334e325a79496765473173626e4d3665477870626d7339496d6830644841364c79393364336375647a4d7562334a6e4c7a45354f546b7665477870626d736949486739496a427765434967655430694d48423449676f4a49485a705a58644362336739496a41674d4341314d5449674e5445794969427a64486c735a5430695a573568596d786c4c574a685932746e636d3931626d5136626d5633494441674d4341314d5449674e5445794f794967654731734f6e4e7759574e6c50534a77636d567a5a584a325a5349253242436a787a64486c735a5342306558426c50534a305a5868304c324e7a63794925324243676b756333517765325a706247773649305a47526b5a47526a7439436a777663335235624755253242436a78775958526f49474e7359584e7a50534a7a6444416949475139496b30304d6a51754e79777a4e7a4d754f574d774c444d334c6a59744e5455754d5377324f4334324c546b794c6a63734e6a67754e6b67784f4441754e474d744d7a63754f5377774c546b794c6a63744d7a41754e7930354d6934334c5459344c6a5a324c544d754e6d677a4d7a59754f56597a4e7a4d754f586f694c7a344b50484268644767675932786863334d39496e4e304d4349675a443069545451794e4334334c4449354d693478534445344d4334305979307a4e7934324c4441744f5449754e79307a4d5330354d6934334c5459344c6a5a324c544d754e6b677a4d7a4a6a4d7a63754e6977774c446b794c6a63734d7a45734f5449754e7977324f433432566a49354d6934786569497650676f38634746306143426a6247467a637a3069633351774969426b50534a4e4e4449304c6a63734d5451784c6a64494f4463754e3359744d793432597a41744d7a63754e6977314e4334344c5459344c6a59734f5449754e7930324f43343253444d7a4d6d4d7a4e7934354c4441734f5449754e79777a4d4334334c446b794c6a63734e6a67754e6c59784e4445754e336f694c7a344b5043397a646d63253242436725334425334426636f6c6f72413d313631363164267374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/43bc52727967f19560c435cded58054c288d74d79a44bfc77f4623756ea56be4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d4275696c74253230576974682532305374656e63696c2d3136313631642e7376673f6c6f676f3d64617461253341696d616765253246737667253242786d6c2533426261736536342532435044393462577767646d567963326c76626a30694d5334774969426c626d4e765a476c755a7a30696458526d4c546769507a344b504345744c5342485a57356c636d4630623349364945466b62324a6c49456c736248567a64484a6864473979494445354c6a49754d53776755315a484945563463473979644342516248566e4c556c754943346755315a4849465a6c636e4e7062323436494459754d444167516e5670624751674d436b674943307450676f3863335a6e49485a6c636e4e7062323439496a45754d53496761575139496b786865575679587a4569494868746247357a50534a6f644852774f693876643364334c6e637a4c6d39795a7938794d4441774c334e325a79496765473173626e4d3665477870626d7339496d6830644841364c79393364336375647a4d7562334a6e4c7a45354f546b7665477870626d736949486739496a427765434967655430694d48423449676f4a49485a705a58644362336739496a41674d4341314d5449674e5445794969427a64486c735a5430695a573568596d786c4c574a685932746e636d3931626d5136626d5633494441674d4341314d5449674e5445794f794967654731734f6e4e7759574e6c50534a77636d567a5a584a325a5349253242436a787a64486c735a5342306558426c50534a305a5868304c324e7a63794925324243676b756333517765325a706247773649305a47526b5a47526a7439436a777663335235624755253242436a78775958526f49474e7359584e7a50534a7a6444416949475139496b30304d6a51754e79777a4e7a4d754f574d774c444d334c6a59744e5455754d5377324f4334324c546b794c6a63734e6a67754e6b67784f4441754e474d744d7a63754f5377774c546b794c6a63744d7a41754e7930354d6934334c5459344c6a5a324c544d754e6d677a4d7a59754f56597a4e7a4d754f586f694c7a344b50484268644767675932786863334d39496e4e304d4349675a443069545451794e4334334c4449354d693478534445344d4334305979307a4e7934324c4441744f5449754e79307a4d5330354d6934334c5459344c6a5a324c544d754e6b677a4d7a4a6a4d7a63754e6977774c446b794c6a63734d7a45734f5449754e7977324f433432566a49354d6934786569497650676f38634746306143426a6247467a637a3069633351774969426b50534a4e4e4449304c6a63734d5451784c6a64494f4463754e3359744d793432597a41744d7a63754e6977314e4334344c5459344c6a59734f5449754e7930324f43343253444d7a4d6d4d7a4e7934354c4441734f5449754e79777a4d4334334c446b794c6a63734e6a67754e6c59784e4445754e336f694c7a344b5043397a646d63253242436725334425334426636f6c6f72413d313631363164267374796c653d666c61742d737175617265" alt="Built With Stencil"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Storybook Stencil Example&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;This is a starter project for building a Web Component library with &lt;a href="https://stenciljs.com/" rel="nofollow noopener noreferrer"&gt;Stencil&lt;/a&gt; and &lt;a href="https://storybook.js.org/" rel="nofollow noopener noreferrer"&gt;Storybook&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;To start clone this repo into a new directory and run the following commands:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;For a step-by-step tutorial how to recreate this from scratch you can follow this series of blog posts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://dev.to/ofhouse/build-a-web-component-library-with-stencil-and-storybook-c27" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/95c572344e0d331e302e01b22709c27cd1475267346e9eb5e326bca705a1bba9/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d36505a377a566d6f2d2d2f635f696d616767615f7363616c652c665f6175746f2c666c5f70726f67726573736976652c685f3530302c715f6175746f2c775f313030302f68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d587a73357137526e2d2d2f635f696d616767615f7363616c65253243665f6175746f253243666c5f70726f6772657373697665253243685f343230253243715f6175746f253243775f313030302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f692f6772786c63766362776b7a746e316833747365732e6a7067" height="240px;"&gt;&lt;br&gt;Part 1: &lt;b&gt;Build a Web Component library with Stencil and Storybook&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="https://dev.to/ofhouse/enhance-your-stencil-web-components-in-storybook-with-knobs-actions-and-jsx-54m4" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/4d5c74369b5d81e9657c04f6ad3a3b5e9133e28854f9cc97c9e444e50e43367f/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d58585f45555933582d2d2f635f696d616767615f7363616c652c665f6175746f2c666c5f70726f67726573736976652c685f3530302c715f6175746f2c775f313030302f68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f70726163746963616c6465762f696d6167652f66657463682f732d2d6c6d4e704c5737592d2d2f635f696d616767615f7363616c65253243665f6175746f253243666c5f70726f6772657373697665253243685f343230253243715f6175746f253243775f313030302f68747470733a2f2f6465762d746f2d75706c6f6164732e73332e616d617a6f6e6177732e636f6d2f692f653870656163726f71687a7039757639727374742e6a7067" height="240px;"&gt;&lt;br&gt;Part 2: &lt;b&gt;Enhance your Stencil Web Components in Storybook with Knobs, Actions and JSX&lt;/b&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storybook.js.org/docs/guides/guide-html/" rel="nofollow noopener noreferrer"&gt;Storybook for HTML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-notes" rel="nofollow noopener noreferrer"&gt;Storybook Addon Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-knobs" rel="nofollow noopener noreferrer"&gt;Storybook Addon Knobs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@storybook/addon-actions" rel="nofollow noopener noreferrer"&gt;Storybook Addon Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Author&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;
&lt;br&gt;
&lt;a href="https://github.com/ofhouse" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F472867%3Fv%3D4" width="100px;"&gt;&lt;br&gt;&lt;b&gt;Felix Haus&lt;/b&gt;&lt;/a&gt;&lt;br&gt;&lt;a href="https://felix.house/" rel="nofollow noopener noreferrer"&gt;Website&lt;/a&gt; • &lt;a href="https://twitter.com/ofhouse" rel="nofollow noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;MIT - see &lt;a href="https://github.com/ofhouse/storybook-stencil-example./LICENSE" rel="noopener noreferrer"&gt;LICENSE&lt;/a&gt; for details.&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ofhouse/storybook-stencil-example" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>stencil</category>
      <category>storybook</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Add a Bash-like autocomplete to your PowerShell</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Fri, 27 Dec 2019 18:54:10 +0000</pubDate>
      <link>https://dev.to/ofhouse/add-a-bash-like-autocomplete-to-your-powershell-4257</link>
      <guid>https://dev.to/ofhouse/add-a-bash-like-autocomplete-to-your-powershell-4257</guid>
      <description>&lt;p&gt;I mostly use Ubuntu or MacOS for development, but recently I switched sides because I needed the power from my Windows gaming machine to start playing around with Blender and Unreal Engine.&lt;/p&gt;

&lt;p&gt;What I missed much when using the PowerShell console was the autocomplete ability which Bash offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When you begin to type a command and hit Tab Bash shows you all available commands which begin with the phrase you typed and you can then select  the one you want with the arrow keys.&lt;/p&gt;

&lt;p&gt;PowerShell by default only completes the command immediately without showing you all available commands.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you already started typing the beginning of a command you can use ↑ / ↓ to cycle through your history to autocomplete the command with the parameters you have used before.&lt;/p&gt;

&lt;p&gt;Cycling through the history with the arrow keys is also supported by PowerShell but it completely ignores what you have already typed and replaces everything with the last used command from the history.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Enable better autocompletion in your PowerShell
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbppf8a6dfjc0i5rwkuju.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbppf8a6dfjc0i5rwkuju.gif" alt="Autocomplete in PowerShell" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thankfully PowerShell already has this functionality built-in provided by a module called &lt;code&gt;PSReadline&lt;/code&gt;.&lt;br&gt;
You simply have to enable it in your PowerShell profile (The PowerShell equivalent to your &lt;code&gt;.bashrc&lt;/code&gt;) 👏:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open or create a new &lt;a href="https://docs.microsoft.com/en-us/powershell/scripting/components/ise/how-to-use-profiles-in-windows-powershell-ise?view=powershell-6#selecting-a-profile-to-use-in-the-windows-powershell-ise" rel="noopener noreferrer"&gt;PowerShell Profile&lt;/a&gt; by typing the following commands directly in your PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create profile when not exist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$PROFILE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;New-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ItemType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$PROFILE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Open the profile with an editor (e.g. good old Notepad)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;ii&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$PROFILE&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CurrentUserAllHosts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;In the editor add the following lines to the profile:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Shows navigable menu of all options when hitting Tab&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-PSReadlineKeyHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Tab&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MenuComplete&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Autocompletion for arrow keys&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-PSReadlineKeyHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UpArrow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HistorySearchBackward&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Set-PSReadlineKeyHandler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DownArrow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HistorySearchForward&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Save the file and reopen your PowerShell, now you can use the advanced autocomplete features.&lt;/p&gt;&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Credit for this goes to StackOverflow users &lt;a href="https://stackoverflow.com/a/37715242/831465" rel="noopener noreferrer"&gt;svick&lt;/a&gt; and &lt;a href="https://stackoverflow.com/a/59334852/831465" rel="noopener noreferrer"&gt;JerryGoyal&lt;/a&gt; which have posted this in their answers.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus: Add the &lt;code&gt;open&lt;/code&gt; command from MacOS to your PowerShell
&lt;/h1&gt;

&lt;p&gt;On MacOS there is the powerful &lt;code&gt;open&lt;/code&gt; command to open folders in Finder (the file explorer from MacOS) or files with their default application from the terminal.&lt;br&gt;
PowerShell also has this command built-in which is called &lt;code&gt;ii&lt;/code&gt; (Which stands for &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-item" rel="noopener noreferrer"&gt;Invoke-Item&lt;/a&gt;).&lt;br&gt;
Because I find the term &lt;code&gt;ii&lt;/code&gt; hard to remember I added an &lt;code&gt;open&lt;/code&gt; alias for it which makes it more convenient when switching between the platforms.&lt;/p&gt;

&lt;p&gt;Adding a new alias to the PowerShell profile is really simple with the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-alias" rel="noopener noreferrer"&gt;&lt;code&gt;New-Alias&lt;/code&gt;&lt;/a&gt; command, so simply edit your profile and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# New-Alias &amp;lt;alias&amp;gt; &amp;lt;aliased-command&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-Alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ii&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it and reopen your PowerShell and test it by typing &lt;code&gt;open .&lt;/code&gt; to open an Windows explorer window in your working directory.&lt;/p&gt;

&lt;h1&gt;
  
  
  Last tip: Use the new Windows Terminal
&lt;/h1&gt;

&lt;p&gt;To make your PowerShell experience even better you should also install the new Windows Terminal preview from the Microsoft store:&lt;br&gt;
&lt;a href="https://www.microsoft.com/store/productId/9N0DX20HK701" rel="noopener noreferrer"&gt;https://www.microsoft.com/store/productId/9N0DX20HK701&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It offers tabs and a better customization than the default console in Windows.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I am still pretty new to PowerShell so if you have other tips to increase the productivity like this I would love when you leave a comment here.&lt;/em&gt; 💖&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>productivity</category>
      <category>windows</category>
    </item>
    <item>
      <title>AngularJS: Hot-Module-Replacement with babel-plugin</title>
      <dc:creator>Felix Haus</dc:creator>
      <pubDate>Sun, 08 Dec 2019 17:25:44 +0000</pubDate>
      <link>https://dev.to/ofhouse/angularjs-hot-module-replacement-with-babel-plugin-176f</link>
      <guid>https://dev.to/ofhouse/angularjs-hot-module-replacement-with-babel-plugin-176f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
In 2020 AngularJS will turn 10 years old and with it's final sunset is only 1.5 years away (on &lt;a href="https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c"&gt;June 30, 2021&lt;/a&gt;) you should not start to write new applications using AngularJS!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Despite its age there are still some AngularJS applications out there.&lt;br&gt;
While the most websites on the internet already have replaced AngularJS by something newer, there are still some pretty large AngularJS applications alive behind the safe firewalls of company-intranets.&lt;br&gt;
So if you still have to deal with AngularJS there is some good news: While upgrading from AngularJS may not be an option for you, you can still give your development experience a decent upgrade by enabling hot-module-replacement on your project.&lt;/p&gt;
&lt;h1&gt;
  
  
  Upgrade your developer experience by using Hot-Module-Replacement
&lt;/h1&gt;

&lt;p&gt;The only requirement for this is that your project is already uses a bundler (like &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt; or &lt;a href="https://parceljs.org/"&gt;Parcel&lt;/a&gt;) which supports hot-module-replacement (HMR) and &lt;a href="https://babeljs.io/"&gt;babel&lt;/a&gt; to transform your source.&lt;/p&gt;

&lt;p&gt;Next install the &lt;code&gt;babel-plugin-ng-hot-reload&lt;/code&gt; package from npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; babel-plugin-ng-hot-reload     &lt;span class="c"&gt;# npm or&lt;/span&gt;
yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; babel-plugin-ng-hot-reload  &lt;span class="c"&gt;# yarn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, update your &lt;code&gt;.babelrc.js&lt;/code&gt; file and add the newly installed plugin:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .babelrc.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babel/preset-env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;- The plugin works with es-modules!&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;babel-plugin-ng-hot-reload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When you are using Parcel no other configuration is necessary.&lt;br&gt;
If you're using webpack, make sure to enable hot-module-replacement in your &lt;code&gt;webpack.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// webpack.config.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development,
  entry: [
    `${require.resolve(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;)}?/`, // &amp;lt;- add
    require.resolve(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;webpack&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;only&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;), // &amp;lt;- add
    &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;
  ],
  output: {
    path: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
    filename: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
    publicPath: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
  },
  module: {
    rules: [
      {
        test: /&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;.js$/,
        loader: [&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;babel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;],
        exclude: /node_modules/,
      },
      ...
    ],
  },
  resolve: {
    extensions: [&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;],
  },
  plugins: [
    ...
    new webpack.HotModuleReplacementPlugin(), // &amp;lt;- add
  ],
  devServer: {
    hot: true, // &amp;lt;- add
  },
};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now you can edit your HTML, CSS or JavaScript files and they should be replaced without a reload of your application.&lt;/p&gt;

&lt;p&gt;Fore some more advanced use-cases here are some examples available on CodeSandbox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://codesandbox.io/s/github/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/javascript-webpack"&gt;Webpack / JavaScript demo on CodeSandbox &lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;code&gt;angular&lt;/code&gt; as global variable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codesandbox.io/s/github/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/typescript-webpack"&gt;Webpack / TypeScript demo on CodeSandbox&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Uses TypeScript babel-preset&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://github.com/schmod/babel-plugin-angularjs-annotate"&gt;&lt;code&gt;angularjs-annotate&lt;/code&gt;&lt;/a&gt; babel-plugin&lt;/li&gt;
&lt;li&gt;Uses Lazy-Loading provided by &lt;a href="https://oclazyload.readme.io/"&gt;&lt;code&gt;ocLazyLoad&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/typescript-parcel"&gt;Parcel / TypeScript demo on GitHub&lt;/a&gt;
(&lt;em&gt;Does not run on CodeSandbox&lt;/em&gt;)

&lt;ul&gt;
&lt;li&gt;Uses Parcel instead of webpack&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Some words about &lt;code&gt;babel-plugin-ng-hot-reload&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;Under the hood the plugin relies on the solid work of the &lt;a href="https://github.com/noppa/ng-hot-reload"&gt;&lt;code&gt;ng-hot-reload&lt;/code&gt;&lt;/a&gt; library by &lt;a href="https://github.com/noppa"&gt;Oskari Noppa&lt;/a&gt;.&lt;br&gt;
Since this library originally provides a webpack-loader the usage was limited to webpack.&lt;br&gt;
Another limitation from the loader is that it is only compatible with &lt;code&gt;commonJS&lt;/code&gt; modules created by babel, so if your are using &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; syntax it gets rewritten to &lt;code&gt;require('...')&lt;/code&gt; and &lt;code&gt;module.exports = {...}&lt;/code&gt; which is incompatible with tree-shaking.&lt;/p&gt;

&lt;p&gt;This babel-plugin takes a deeper look at your code by preserving the &lt;code&gt;import&lt;/code&gt;s and &lt;code&gt;export&lt;/code&gt;s and only adds the code required for the hot-reload to the modules which are interacting with angular.&lt;br&gt;
So the code for the browser is less bloated and can use the same tree-shaking like the production version (Lazy-loaded AngularJS modules are also supported by the plugin).&lt;/p&gt;

&lt;p&gt;If you want to know more, the the plugin and the examples are open source and available here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ofhouse"&gt;
        ofhouse
      &lt;/a&gt; / &lt;a href="https://github.com/ofhouse/babel-plugin-ng-hot-reload"&gt;
        babel-plugin-ng-hot-reload
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Hot reloading for AngularJS apps via babel plugin.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
🔥 babel-plugin-ng-hot-reload &lt;a href="https://travis-ci.org/ofhouse/babel-plugin-ng-hot-reload" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c694bfc36a04da0c3fec5430bfb9cfec48c39d15/68747470733a2f2f7472617669732d63692e6f72672f6f66686f7573652f626162656c2d706c7567696e2d6e672d686f742d72656c6f61642e7376673f6272616e63683d6d6173746572" alt="Build status"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;A babel plugin which enables hot module replacement in AngularJS applications.&lt;br&gt;
The plugin is based on the &lt;a href="https://github.com/noppa/ng-hot-reload"&gt;ng-hot-reload&lt;/a&gt; webpack loader but is rewritten as babel plugin so that it is compatible with es-module syntax and every bundler which supports the hot-replacement API (e.g. &lt;a href="https://webpack.js.org/" rel="nofollow"&gt;webpack&lt;/a&gt; or &lt;a href="https://parceljs.org/" rel="nofollow"&gt;parcel&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;
Getting started&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;To see it in action you can simply checkout the examples on CodeSandbox:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/github/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/javascript-webpack" rel="nofollow"&gt;Webpack / JavaScript demo on CodeSandbox &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codesandbox.io/s/github/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/typescript-webpack" rel="nofollow"&gt;Webpack / TypeScript demo on CodeSandbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codesandbox.io/s/github/ofhouse/babel-plugin-ng-hot-reload/tree/master/examples/typescript-parcel" rel="nofollow"&gt;Parcel / TypeScript demo on CodeSandbox&lt;/a&gt; (There is an issue with HTML import, see: &lt;a href="https://raw.githubusercontent.com/ofhouse/babel-plugin-ng-hot-reload/master/#known-issues-with-parcel"&gt;FAQ&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;npm i -D babel-plugin-ng-hot-reload     &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; npm or&lt;/span&gt;
yarn add -D babel-plugin-ng-hot-reload  &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; yarn&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add the following to your &lt;code&gt;babelrc.js&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// without options&lt;/span&gt;
&lt;span class="pl-smi"&gt;module&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;exports&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;plugins&lt;/span&gt;: &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s"&gt;'babel-plugin-ng-hot-reload'&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-c"&gt;// with options&lt;/span&gt;
&lt;span class="pl-smi"&gt;module&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;exports&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-c1"&gt;plugins&lt;/span&gt;: &lt;span class="pl-kos"&gt;[&lt;/span&gt;
    &lt;span class="pl-kos"&gt;[&lt;/span&gt;
      &lt;span class="pl-s"&gt;'babel-plugin-ng-hot-reload'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
      &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-c1"&gt;angularGlobal&lt;/span&gt;: &lt;span class="pl-c1"&gt;false&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
        &lt;span class="pl-c1"&gt;forceRefresh&lt;/span&gt;: &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ofhouse/babel-plugin-ng-hot-reload"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>angular</category>
      <category>babel</category>
      <category>webpack</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
