<?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>Forem: Chimezie Enyinnaya</title>
    <description>The latest articles on Forem by Chimezie Enyinnaya (@mezie).</description>
    <link>https://forem.com/mezie</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%2F25506%2F0d7d8958-ac94-4db4-a026-dc0e5f0f33d4.jpg</url>
      <title>Forem: Chimezie Enyinnaya</title>
      <link>https://forem.com/mezie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mezie"/>
    <language>en</language>
    <item>
      <title>Using npm packages in AdonisJS</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Sun, 11 Aug 2024 07:07:19 +0000</pubDate>
      <link>https://forem.com/mezie/using-npm-packages-in-adonisjs-cnj</link>
      <guid>https://forem.com/mezie/using-npm-packages-in-adonisjs-cnj</guid>
      <description>&lt;p&gt;One of the most asked questions in the AdonisJS community has to be about how to use X package in AdonisJS. Those asking this question tend to forget that AdonisJS is a Node.js framework and as a result, we can use an npm package the same way we'd use it in any Node.js project.&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll show two ways of using an npm package in AdonisJS. I'll use the &lt;a href="https://github.com/PaddleHQ/paddle-node-sdk" rel="noopener noreferrer"&gt;Paddle Node.js SDK&lt;/a&gt; to demonstrate this but the same principles apply to other npm packages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the package directly
&lt;/h2&gt;

&lt;p&gt;The simplest way to use an npm package in AdonisJS is to install the package and use it directly wherever we want to use it in our codebase.&lt;/p&gt;

&lt;p&gt;We need to first install the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @paddle/paddle-node-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say we are working with the concept of customer and we have a &lt;code&gt;CustomersController&lt;/code&gt;. First, we import the package and then use it in the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/controllers/customers_controller.ts&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;Paddle&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;@paddle/paddle-node-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&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;@adonisjs/core/services/config&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContext&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;paddle&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;Paddle&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;services.paddle.apiKey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;paddle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paddleId&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;h2&gt;
  
  
  Using a service class
&lt;/h2&gt;

&lt;p&gt;The problem with the above approach is that we end up littering our codebase with the same package import (and initialisation) over and over. For some packages that could be okay. For example, using a package like &lt;code&gt;lodash&lt;/code&gt;. But with the package and concept we are working with, that might not be the case.&lt;/p&gt;

&lt;p&gt;So to take the above implementation one step further, we could create a &lt;code&gt;PaddleService&lt;/code&gt;. We could create the service manually but there's a command for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node ace make:service paddle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create an empty &lt;code&gt;paddle_service.ts&lt;/code&gt; file inside &lt;code&gt;app/services&lt;/code&gt;. Add the following code inside the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/services/paddle_service.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&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;@adonisjs/core/services/config&lt;/span&gt;&lt;span class="dl"&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;Customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Paddle&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;@paddle/paddle-node-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;app&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;@adonisjs/core/services/app&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaddleService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;paddle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Paddle&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&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;paddle&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;Paddle&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="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;services.paddle.apiKey&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;async&lt;/span&gt; &lt;span class="nf"&gt;getCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;paddle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&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;Like before, we import the package and initiate it. For the usage, we create a method that we can use in controllers or other places. Now, we can keep all Paddle-specified implementations contained in one place.&lt;/p&gt;

&lt;p&gt;We can make use of the service class in two ways. First, we import and initialise the class manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/controllers/customers_controller.ts&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;inject&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;@adonisjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PaddleService&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;#services/paddle_service&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContext&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;paddle&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;PaddleService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;paddle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paddleId&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;The other way is to leverage AdonisJS &lt;a href="https://docs.adonisjs.com/guides/concepts/dependency-injection" rel="noopener noreferrer"&gt;Dependency Injection (DI)&lt;/a&gt; to initialise the class with the necessary dependencies. We can do that using the &lt;code&gt;@inject&lt;/code&gt; decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/controllers/customers_controller.ts&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;inject&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;@adonisjs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PaddleService&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;#services/paddle_service&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomersController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paddle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PaddleService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;paddle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paddleId&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;Here, we are using method injection but we could also use constructor injection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So that's how to use npm packages in AdonisJS. Remember, AdonisJS is just a Node.js framework.&lt;/p&gt;

</description>
      <category>adonisjs</category>
    </item>
    <item>
      <title>Add Alpine.js to your Node Application</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Thu, 28 Oct 2021 17:34:24 +0000</pubDate>
      <link>https://forem.com/sanity-io/add-alpinejs-to-your-node-application-1c4h</link>
      <guid>https://forem.com/sanity-io/add-alpinejs-to-your-node-application-1c4h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to Alpine.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://alpinejs.dev/start-here" rel="noopener noreferrer"&gt;Alpine.js&lt;/a&gt; is a modern, small and lightweight, CDN-first JavaScript framework for composing JavaScript behavior in your markup. It borrows its concepts from other frameworks like Vue, React, and jQuery. Think of it like &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; for JavaScript in the sense that it allows you to write most of your JavaScript inline in your HTML, making it easier to write declarative code.&lt;/p&gt;

&lt;p&gt;Alpine.js is not designed to build SPAs, but rather to enhance your templates with a little bit of JavaScript. Alpine.js is designed to be used as a direct script include from a public CDN.&lt;/p&gt;

&lt;p&gt;The main power of Alpine.js is its reactive nature. You use its directives to bind data and any changes made to it reverberate in the whole app regardless of the level of the emanation. Alpine.js is more like a UI interaction framework.&lt;/p&gt;

&lt;p&gt;Alpine.js is also a DOM manipulation library. It makes it easy for us to interact with the DOM in a declarative way with lesser code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why choose Alpine.js
&lt;/h2&gt;

&lt;p&gt;According to the creator, Caleb Porzio:&lt;/p&gt;

&lt;p&gt;Alpine.js offers you the reactive and declarative nature of big frameworks like Vue or React at a much lower cost. You get to keep your DOM and sprinkle in behavior where it is needed.&lt;/p&gt;

&lt;p&gt;Alpine.js works well when you need a small amount of JavaScript, e.g. a few drop-downs or tabs. This means you get a bunch of power at a crazy small size with no need to npm install.&lt;/p&gt;

&lt;p&gt;So, here are some reasons why you should consider using Alpine.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Size:&lt;/strong&gt; In the introduction, I stated that Alpine.js is small and lightweight. It has the power of big frameworks yet has a small bundle size. Alpine.js comes in at &lt;a href="https://bundlephobia.com/result?p=alpinejs" rel="noopener noreferrer"&gt;21.9kB minified&lt;/a&gt; and 7.1kB gzipped, compared to jQuery at &lt;a href="https://bundlephobia.com/result?p=jquery" rel="noopener noreferrer"&gt;87.6kB minified&lt;/a&gt; and 30.4kB minified and gzipped. Only 23% the size!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Installation and learning:&lt;/strong&gt; Installing Alpine is pretty straightforward. You’ll just need to add a script tag linking the library. That’s it. Really. You don’t need to compile your JavaScript or structure your project in any specific way; this makes it the perfect candidate for integration with existing projects*&lt;em&gt;.&lt;/em&gt;* You also have an npm package available if your project is already using npm.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its learning curve is also very low. The concepts and syntax behind the framework are heavily inspired by Vue.js. If you’re coming from a Vue background, you’ll most likely feel at home working with Alpine.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic syntax
&lt;/h2&gt;

&lt;p&gt;Alpine.js syntax is very much inspired by &lt;a href="https://v3.vuejs.org/guide/introduction.html" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;.  Like Vue, Alpine uses an &lt;code&gt;x-&lt;/code&gt; prefix on all its directives. Unlike Vue, Alpine.js doesn’t use a virtual DOM, so we get to work with the actual DOM directly. Alpine.js currently has 15 directives and 6 magic properties that become building blocks for many user interface scenarios. These directives allow you to do most of the things you would do with a more robust framework like templating, view/model data binding, UI transitions, and simplified event handling, all in a tiny package and directly in your HTML.&lt;/p&gt;

&lt;p&gt;Let's go over a few directives:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"[JSON data object]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive declares a new component scope. It tells the framework to initialize a new component with the following data object. Think of it as the data property of a Vue component. Only children of an element can access the &lt;code&gt;x-data&lt;/code&gt; value declared on the element. Sibling elements, adjacent elements cannot access it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-show&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"[expression]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive toggles the visibility of components. It works the manipulating the CSS display style property of the element. The directive takes an expression, and the expression must resolve to a boolean value(true/false). If true, the element is shown by setting its CSS display property to a value other than none. If false, the display property of the element is set to none.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-bind&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-bind:[attribute]=&lt;/span&gt;&lt;span class="s"&gt;"[expression]"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; ...&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive is used to set the value of an element's attribute. The expression is evaluated and the results of the evaluation become the value of the specified attribute.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-text&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"[expression]"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive is used to set the inner text node of an element. The directive uses the innerText DOM property on the element to achieve this.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-if&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;x-if=&lt;/span&gt;&lt;span class="s"&gt;"[expression]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive conditionally renders elements based on the condition of their expression. If the condition is true, the element is visible. If the condition is false, the element is not visible. &lt;code&gt;x-if&lt;/code&gt; does not use the display property for its conditional rendering, it removes or adds the elements to the DOM. Alpine.js says it is best to use the &lt;code&gt;x-if&lt;/code&gt; directive in tags, this is because Alpine.js uses the real DOM, not a virtual DOM.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-for&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;x-for=&lt;/span&gt;&lt;span class="s"&gt;"[data] in [data]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;div&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive is used to iterate over the items in an array and create new DOM nodes for each item. Just like &lt;code&gt;x-if&lt;/code&gt;, &lt;code&gt;x-for&lt;/code&gt; is used in the template tag.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-on&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-on:[event]=&lt;/span&gt;&lt;span class="s"&gt;"[expression]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive attaches event listeners to the element it is set upon. When the event is emitted the JavaScript expression in the directive is run.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-model&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-model=&lt;/span&gt;&lt;span class="s"&gt;"[data item]"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This directive is used for two-way binding. Most especially used in the input elements. What is two-way binding? A bound variable in the HTML view can be changed from the script and also from the HTML view. From either side the change occurred, both the script side and HTML view bound-data are updated.&lt;/p&gt;

&lt;p&gt;To learn more about Alpine.js directives and Alpine.js as a whole, check out the &lt;a href="https://alpinejs.dev/start-here" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. We will use it in building our components much later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the sanity schema and studio
&lt;/h2&gt;

&lt;p&gt;First of all, create a new project, you can either create a new project in your terminal by this command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;or from the official website &lt;a href="https://sanity.io/get-started" rel="noopener noreferrer"&gt;https://sanity.io/get-started&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BIb709D8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropbox.com/s_8BA0C9EE4280FF7A41DC3D893FA5FD7386CAA2804E78C6B0C90771284A6A47D2_1622506741505_Screenshot%2B12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BIb709D8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://paper-attachments.dropbox.com/s_8BA0C9EE4280FF7A41DC3D893FA5FD7386CAA2804E78C6B0C90771284A6A47D2_1622506741505_Screenshot%2B12.png" alt="Sanity homepage" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we will be using the Sanity CLI. If you don’t have it installed already, you can set it up globally on your local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @sanity/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proceed to create a new project, using:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;To create a new project, you need a Sanity.io account and you will be prompted to log in or create a new account, simply follow the instructions to set it up.&lt;/p&gt;

&lt;p&gt;After successful authentication, you can then set a project name and use the default dataset configuration if you want to and confirm the project path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a8gq0t8xik6h7j0vxoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a8gq0t8xik6h7j0vxoy.png" alt="Creating a new project with Sanity CLI" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next option is quite important, you decide to use a project template with schema and sample data or start a clean project with no predefined schema. Here, I will be choosing the movie project template so we can use the data that comes with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5if8hmvvu14l26peeui2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5if8hmvvu14l26peeui2.png" alt="Creating a new project with Sanity CLI (continued)" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we get a success message, we can then proceed to work on our schema for the project. Open the folder in your code editor and run the sanity studio in your terminal,&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This will open up the studio in our browser on &lt;a href="http://localhost:3333/" rel="noopener noreferrer"&gt;http://localhost:3333/&lt;/a&gt;. You will be required to log in again.&lt;/p&gt;

&lt;p&gt;We now have data from the movie project we started with and it already has 3 schemas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1isxpul8oxtg9gq260t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx1isxpul8oxtg9gq260t.png" alt="Sanity studio" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if we started with a clean project without a schema? Let’s create a new schema so we can learn how it is done.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;schemas&lt;/code&gt; folder in our project folder and create a new file, let’s call it &lt;code&gt;trending.js&lt;/code&gt;. This will hold trending movies.&lt;/p&gt;

&lt;p&gt;In our new schema file, let’s export a default object and give it a name, title, and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// schemas/trending.js&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trending&lt;/span&gt;&lt;span class="dl"&gt;'&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;Trending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now add fields to our new schema, this schema is supposed to show trending videos so let's add a field array to reflect that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// schemas/trending.js&lt;/span&gt;

&lt;span class="nx"&gt;fields&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&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;Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;releaseDate&lt;/span&gt;&lt;span class="dl"&gt;'&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;Release date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;datetime&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;popularity&lt;/span&gt;&lt;span class="dl"&gt;'&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;Popularity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poster&lt;/span&gt;&lt;span class="dl"&gt;'&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;Poster Image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hotspot&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding the fields, we need to let Sanity know that we have created a new schema. Head over to &lt;code&gt;schema.js&lt;/code&gt; and import the new schema and add it to the &lt;code&gt;schemaTypes&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// schemas/schema.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;trending&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;./trending&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;createSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...,&lt;/span&gt;
  &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&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="p"&gt;...,&lt;/span&gt;
    &lt;span class="nx"&gt;trending&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, if you go back to the studio running in the browser, the new schema is created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54mtk5866101f41x4whz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54mtk5866101f41x4whz.png" alt="Sanity studio showing new schema" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is how to create a schema in Sanity, though we won't actually be needing it for the rest of the tutorial.&lt;/p&gt;

&lt;p&gt;So far, we have successfully set up Sanity schema and studio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Alpine.js
&lt;/h2&gt;

&lt;p&gt;Remember I stated earlier that we will use Alpine.js for our frontend, let’s create components with Alpine.js that we need for this example. Create a &lt;code&gt;frontend&lt;/code&gt; directory and inside it create an &lt;code&gt;index.html&lt;/code&gt; file. To run the frontend, I'll be using VS Code's &lt;a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer" rel="noopener noreferrer"&gt;Live Server&lt;/a&gt; and the app will be ruining on &lt;a href="http://127.0.0.1:5500/frontend/" rel="noopener noreferrer"&gt;http://127.0.0.1:5500/frontend&lt;/a&gt;. If you're not using VS Code, you might need to Google how to run a live server for your editor.&lt;/p&gt;

&lt;p&gt;To add Alpine.js to a project, you can either make use of the CDN or through npm.&lt;/p&gt;

&lt;p&gt;To use CDN, add the CDN link to the head section, It will initialize itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production environments, To avoid unexpected behaviors and unexpected breakage from newer versions from Alpine.js, ensure you specify pin a specific version number in the CDN link. For example, to use version 3.4.2 (latest):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/alpinejs@3.4.2/dist/cdn.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use npm, run the command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then import it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alpinejs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the project we'll be building in this tutorial, we'll be using the CDN by adding the &lt;code&gt;script&lt;/code&gt; tag to your &lt;code&gt;index.html&lt;/code&gt; page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- frontend/index.html --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Alpine.js and Sanity.io&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/alpinejs@3.4.2/dist/cdn.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exploring Alpine.js Components
&lt;/h2&gt;

&lt;p&gt;First, let’s create a small toggle functionality to learn how Alpine.js works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- frontend/index.html --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Alpine.js and Sanity.io&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.2/
        dist/alpine.min.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This is a toggle button&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Toggle&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Tadah! I am Visible&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the code above, Alpine.js CDN was added to the head section and the body includes a &lt;code&gt;div&lt;/code&gt; with a text and button, the second div contains the content we are going to toggle. Let's add some Alpine.js code to the code above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ isOpen: false }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This is a toggle button&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Toggle&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Tadah! I am Visible&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here, the &lt;code&gt;x-data&lt;/code&gt; directive is used to give state to the entire component and displays the second &lt;code&gt;div&lt;/code&gt; when the value of &lt;code&gt;isOpen&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. The &lt;code&gt;x-show&lt;/code&gt; simply shows the div when it's true. Let’s make the button togglable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ isOpen: false }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This is a toggle button&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;x-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = !isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Toggle&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Tadah! I am Visible&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it works! Using the &lt;code&gt;x-on&lt;/code&gt;, &lt;code&gt;x-show&lt;/code&gt;, and &lt;code&gt;x-data&lt;/code&gt; directives, we were able to create an interactive toggle.&lt;/p&gt;

&lt;p&gt;Let’s try creating the other components before we work with our sanity data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performing interactions with Alpine.js
&lt;/h2&gt;

&lt;p&gt;Let’s add a responsive navbar with more emphasis on applying interactions to its mobile view using Alpine.js. We will use Tailwind CSS for the styling. Tailwind CSS is a utility-first CSS framework packed with classes that can be composed to build any design, directly in your markup.&lt;/p&gt;

&lt;p&gt;Like Alpine.js, it can also be added to a project using its CDN. Include it in the head section&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"&lt;/span&gt; 
&lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we start with a nav tag to contain all the code for the navigation bar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between flex-wrap p-6 fixed w-full z-10 top-0 bg-indigo-900"&lt;/span&gt;
    &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ isOpen: false }"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;keydown.escape=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = false"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how &lt;code&gt;x-data&lt;/code&gt; is used to set the default state of is open to false, this tells Alpine.js to keep the default state of the navbar closed. The &lt;code&gt;@keydown.escape&lt;/code&gt; tells Alpine.js to set the navbar to close when there is a keydown press outside the navbar. Let’s add a button that will only be visible on mobile screens. Add the code snippet below inside &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
  &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = !isOpen"&lt;/span&gt;
  &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block lg:hidden px-2 text-gray-500 hover:text-white focus:outline-none focus:text-white"&lt;/span&gt;
  &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'transition transform-180': isOpen }"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-6 w-6 fill-current"&lt;/span&gt;
    &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
    &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt;
      &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;
      &lt;span class="na"&gt;fill-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt;
      &lt;span class="na"&gt;clip-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt;
      &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt;
      &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"!isOpen"&lt;/span&gt;
      &lt;span class="na"&gt;fill-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt;
      &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The button contains a hamburger toggle that opens and closes when the button is clicked. It also has a class that hides in large screens. Finally, let’s create the menu just after the button above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full flex-grow lg:flex lg:items-center lg:w-auto"&lt;/span&gt;
  &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'block shadow-3xl': isOpen, 'hidden': !isOpen }"&lt;/span&gt;
  &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.away=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = false"&lt;/span&gt;
    &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;
  &lt;span class="na"&gt;x-transition&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pt-6 lg:pt-0  lg:flex justify-end flex-1 items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block py-2 px-4 text-white"&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Movies&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt;
            &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Contact&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have been able to build a responsive navigation bar (you will need to resize your browser window to see the button) using Alpine.js and Tailwind CSS for styling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performing HTTP request
&lt;/h2&gt;

&lt;p&gt;Let’s create the body component of our movie app.&lt;/p&gt;

&lt;p&gt;To perform HTTP requests from sanity, we need to get an API from sanity studio. To do that, go back to &lt;code&gt;[http://localhost:3333](http://localhost:3333)&lt;/code&gt;. Just in case you have closed it, run the command again in your terminal.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Click on the Vision tab, the environment is divided into 3 sections, the query section for trying out search queries, and the result section displays results from the query search. The params sections are optional to use if you have specific parameters you want to add. Note that the query is based on the data you have in your dataset in Sanity. The default dataset is production, as we set it during the project creation but we can create another dataset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F433ejoepmgh01e8neo3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F433ejoepmgh01e8neo3y.png" alt="Sanity studio dataset" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sanity uses GROQ as its query language, you can read more about GROQ &lt;a href="https://www.sanity.io/docs/groq" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Let’s search for movies in the query section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'movie'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nev0t3kztp3mne1tm6x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nev0t3kztp3mne1tm6x.png" alt="Sanity studio GRPQ" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon running the query, a query URL was generated for us. This URL can be used to fetch data from sanity into our Alpine.js frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling CORS
&lt;/h2&gt;

&lt;p&gt;Before we can be able to fetch data from sanity into our Alpine.js frontend, we need to enable CORS. That is, we need to add the URL the frontend app is running on as part of the hosts that can connect to the project API. We can do that from the API tab on your &lt;a href="https://www.sanity.io/manage" rel="noopener noreferrer"&gt;project settings&lt;/a&gt; as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmq98bh1kwfva4u7e5ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmq98bh1kwfva4u7e5ym.png" alt="Enable CORS in Sanity project settings" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the &lt;code&gt;index.html&lt;/code&gt; file, create a &lt;code&gt;main&lt;/code&gt; tag that will house the whole component, then create a container with an &lt;code&gt;h2&lt;/code&gt; tag with some text. This is optional. Afterward, create a &lt;code&gt;div&lt;/code&gt;, this &lt;code&gt;div&lt;/code&gt; will carry out the HTTP request using fetch, and we will wrap our fetch request in &lt;code&gt;x-init&lt;/code&gt; so that it loads immediately after the page is loaded. Like a typical fetch request, the URL will be the query URL generated from the studio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-32"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container mx-auto px-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Hello&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
      &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"init()"&lt;/span&gt;
      &lt;span class="na"&gt;x-init=&lt;/span&gt;&lt;span class="s"&gt;"fetch('https://nhksjapu.api.sanity.io/v2021-03-25/data/query/production?query=*%5B_type%20%3D%3D%20%22movie%22%5D%20%7B%20title%2C%20releaseDate%2C%20%22imageUrl%22%3A%20poster.asset-%3Eurl%20%7D')
            .then(response =&amp;gt; response.json())
            .then(response =&amp;gt; movies = response.result)"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-5 flex flex-wrap justify-center"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are running the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;_type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"movie"&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="err"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;releaseDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;poster.asset-&amp;gt;url&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of returning everything from the query, we specify we only want the title, release date, and lastly the image URL. Since the image URL is nested inside &lt;code&gt;poster&lt;/code&gt; then &lt;code&gt;asset&lt;/code&gt;, so we simply assign it to a new variable (&lt;code&gt;imageURL&lt;/code&gt;) for easy access.&lt;/p&gt;

&lt;p&gt;The application state is set on the &lt;code&gt;div&lt;/code&gt; tag by the &lt;code&gt;x-data&lt;/code&gt; directive using a function called &lt;code&gt;init()&lt;/code&gt;. This function will be created soon. The &lt;code&gt;x-int&lt;/code&gt; fetches a list of movies from sanity and sets it as the value of the movie.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 gap-6 md:grid-cols-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;x-for=&lt;/span&gt;&lt;span class="s"&gt;"(movie, index) in movies"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;x-bind:src=&lt;/span&gt;&lt;span class="s"&gt;"movie.imageUrl"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-semibold"&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"movie.title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-gray-500"&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"movie.releaseDate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This piece of code displays the results of the query. We used the &lt;code&gt;x-for&lt;/code&gt; directive to display the movies stored in the movies array. The template inside the &lt;code&gt;x-for&lt;/code&gt; holds how each movie will be displayed. Also, we used &lt;code&gt;x-text&lt;/code&gt; to set the innerText to display the movie title and year of each movie. We used &lt;code&gt;x-bind:src&lt;/code&gt; to bind the src attribute of the image to the image URL of the movie, this makes the movie image to be rendered.&lt;/p&gt;

&lt;p&gt;Let’s add a script tag just below the main tag, it will hold the function called by the &lt;code&gt;x-data&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&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;{&lt;/span&gt;
        &lt;span class="na"&gt;movies&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The empty movie array stores all the data gotten from the query.&lt;/p&gt;

&lt;p&gt;Let’s put everything together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- frontend/index.html --&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Alpine.js and Sanity.io&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/alpinejs@3.4.2/dist/cdn.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between flex-wrap p-6 fixed w-full z-10 top-0 bg-indigo-900"&lt;/span&gt;
      &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ isOpen: false }"&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;keydown.escape=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = false"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--Logo --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center flex-shrink-0 text-white mr-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-white no-underline hover:text-white hover:no-underline"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl pl-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"em em-grinning"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt; ecodata Movies&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!--Toggle button --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
            &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = !isOpen"&lt;/span&gt;
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block lg:hidden px-2 text-gray-500 hover:text-white focus:outline-none focus:text-white"&lt;/span&gt;
      &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'transition transform-180': isOpen }"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-6 w-6 fill-current"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt; &lt;span class="na"&gt;fill-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt; &lt;span class="na"&gt;clip-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt;
          &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"!isOpen"&lt;/span&gt; &lt;span class="na"&gt;fill-rule=&lt;/span&gt;&lt;span class="s"&gt;"evenodd"&lt;/span&gt;
          &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- Menu --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full flex-grow lg:flex lg:items-center lg:w-auto"&lt;/span&gt;
      &lt;span class="na"&gt;:class=&lt;/span&gt;&lt;span class="s"&gt;"{ 'block shadow-3xl': isOpen, 'hidden': !isOpen }"&lt;/span&gt;
            &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click.away=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = false"&lt;/span&gt;
            &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;
            &lt;span class="na"&gt;x-transition&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pt-6 lg:pt-0  lg:flex justify-end flex-1 items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block py-2 px-4 text-white"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Movies&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;About&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-block text-gray-300 hover:text-gray-200 py-2 px-4"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Contact&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-32"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container mx-auto px-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
        &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"init()"&lt;/span&gt;
        &lt;span class="na"&gt;x-init=&lt;/span&gt;&lt;span class="s"&gt;"fetch('https://nhksjapu.api.sanity.io/v2021-03-25/data/query/production?query=*%5B_type%20%3D%3D%20%22movie%22%5D%20%7B%20title%2C%20releaseDate%2C%20%22imageUrl%22%3A%20poster.asset-%3Eurl%20%7D')
          .then(response =&amp;gt; response.json())
          .then(response =&amp;gt; movies = response.result)"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-5 flex flex-wrap justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 gap-6 md:grid-cols-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;x-for=&lt;/span&gt;&lt;span class="s"&gt;"(movie, index) in movies"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;x-bind:src=&lt;/span&gt;&lt;span class="s"&gt;"movie.imageUrl"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-semibold"&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"movie.title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
                  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-gray-500"&lt;/span&gt; &lt;span class="na"&gt;x-text=&lt;/span&gt;&lt;span class="s"&gt;"movie.releaseDate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{ isOpen: false }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;This is a toggle button&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;x-on:click=&lt;/span&gt;&lt;span class="s"&gt;"isOpen = !isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Toggle&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-show=&lt;/span&gt;&lt;span class="s"&gt;"isOpen"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Tadah! I am Visible&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&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;{&lt;/span&gt;
        &lt;span class="na"&gt;movies&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it’s a wrap, we have been able to use Alpine.js as the frontend of our movie app with Sanity as our back end. You can explore and add more components to make it look even better.&lt;/p&gt;

&lt;p&gt;You can check the entire project on &lt;a href="https://github.com/ammezie/alpinejs-sanityio" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we covered an introduction to Alpine.js and Sanity.io. We touched on schemas, the Sanity Studio, built a toggle component, a responsive navbar with Alpine.js, and a simple movie gallery component with the framework using Sanity as the backend. Although Alpine.js might not totally replace other frameworks, it can be used in combination with React or Vue to quickly prototype components without writing much JavaScript and Sanity is a wonderful tool to handle data without much hassle.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sanity.io/" rel="noopener noreferrer"&gt;Sanity.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alpinejs/alpine" rel="noopener noreferrer"&gt;Alpine.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>GraphQL from Ground Up</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Mon, 22 Jul 2019 14:09:20 +0000</pubDate>
      <link>https://forem.com/mezie/graphql-from-ground-up-2el5</link>
      <guid>https://forem.com/mezie/graphql-from-ground-up-2el5</guid>
      <description>&lt;p&gt;I'm glad to announce my latest course &lt;em&gt;&lt;a href="https://tutstack.io/courses/graph-ql-from-ground-up"&gt;GraphQL from Ground up&lt;/a&gt;&lt;/em&gt; is now available for early access! This is a practical course where you’ll learn GraphQL by building a fully-featured API for a forum completely scratch.&lt;/p&gt;

&lt;p&gt;The course cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's GraphQL and its improvements over REST&lt;/li&gt;
&lt;li&gt;Understand GraphQL schema and type system&lt;/li&gt;
&lt;li&gt;Understand GraphQL scalar types&lt;/li&gt;
&lt;li&gt;Create custom scalar types&lt;/li&gt;
&lt;li&gt;Understand resolvers&lt;/li&gt;
&lt;li&gt;Integrate database with GraphQL&lt;/li&gt;
&lt;li&gt;Understand how GraphQL operations such as query, mutation and subscription works&lt;/li&gt;
&lt;li&gt;Handle file uploads in GraphQL&lt;/li&gt;
&lt;li&gt;Authentication and authorization&lt;/li&gt;
&lt;li&gt;Understand GraphQL directives&lt;/li&gt;
&lt;li&gt;Create custom directives&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Query optimization with Dataloader&lt;/li&gt;
&lt;li&gt;... and much more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take advantage of the early access price and get it for just $10.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://tutstack.io/courses/graph-ql-from-ground-up"&gt;Get the course&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>node</category>
      <category>apolloserver</category>
      <category>apollographql</category>
    </item>
    <item>
      <title>Deploying Static Site To Netlify</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Thu, 15 Mar 2018 09:18:53 +0000</pubDate>
      <link>https://forem.com/mezie/deploying-static-site-to-netlify--3760</link>
      <guid>https://forem.com/mezie/deploying-static-site-to-netlify--3760</guid>
      <description>&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fokotjt36infmmq4cgcir.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fokotjt36infmmq4cgcir.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll look at how to deploy our static blog built with Nuxt.js and Contentful to Netlify:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/vkoVJkWb84A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Related tutorial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/mezie/build-a-static-blog-with-nuxtjs-and-contentful--202l"&gt;Build A Static Blog With Nuxt.js And Contentful&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>contentful</category>
      <category>netlify</category>
    </item>
    <item>
      <title>Build A Static Blog With Nuxt.js And Contentful</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Thu, 15 Mar 2018 09:16:00 +0000</pubDate>
      <link>https://forem.com/mezie/build-a-static-blog-with-nuxtjs-and-contentful--202l</link>
      <guid>https://forem.com/mezie/build-a-static-blog-with-nuxtjs-and-contentful--202l</guid>
      <description>&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj01b72x0p1b1xseaxi4s.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fj01b72x0p1b1xseaxi4s.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we look at how to build a static blog with Nuxt.js and Contentful:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Pa5t8KyEORs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The code for this tutorial is available on &lt;a href="https://github.com/tutstack/nuxt-static-blog" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Related tutorial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/mezie/deploying-static-site-to-netlify--3760"&gt;Deploying Static Site To Netlify&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nuxt</category>
      <category>vue</category>
      <category>contentful</category>
    </item>
    <item>
      <title>AdonisJs Lucid Relationships</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Fri, 09 Mar 2018 13:17:56 +0000</pubDate>
      <link>https://forem.com/mezie/-adonisjs-lucid-relationships--3110</link>
      <guid>https://forem.com/mezie/-adonisjs-lucid-relationships--3110</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.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%2Fs9qcqavmwxr04ff7ed5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fs9qcqavmwxr04ff7ed5z.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this series, we'll look at how to work with Lucid relationships. We'll start by covering how to define each of the relationships types supported by Lucid. Then we'll look at how to perform database operations with each relationship.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PD-kdpIbUEM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Related tutorial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/mezie/-getting-started-with-lucid-adonisjs-orm--2258"&gt;Getting Started With Lucid: AdonisJs ORM&lt;/a&gt;&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
    </item>
    <item>
      <title>Getting Started With Lucid: AdonisJs ORM</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Fri, 09 Mar 2018 13:12:54 +0000</pubDate>
      <link>https://forem.com/mezie/-getting-started-with-lucid-adonisjs-orm--2258</link>
      <guid>https://forem.com/mezie/-getting-started-with-lucid-adonisjs-orm--2258</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.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%2Fvp0xfgbjkf4t7k55sa30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fvp0xfgbjkf4t7k55sa30.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lucid is AdonisJs ORM, which is an implementation of the active record pattern. In this tutorial, I show you how to get started with Lucid.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CxeQexPV-M8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Related tutorial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/mezie/-adonisjs-lucid-relationships--3110"&gt;AdonisJs Lucid Relationships&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/mezie/whats-new-in--adonisjs-41--557a"&gt;What's New In AdonisJs 4.1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
    </item>
    <item>
      <title>What's New In AdonisJs 4.1</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Fri, 09 Mar 2018 12:57:33 +0000</pubDate>
      <link>https://forem.com/mezie/whats-new-in--adonisjs-41--557a</link>
      <guid>https://forem.com/mezie/whats-new-in--adonisjs-41--557a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.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%2Fob33b3jw1xn348ebuay4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fob33b3jw1xn348ebuay4.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AdonisJs 4.1 was released not too long ago. In this video, I will walk you through the improvements and changes added in the latest version of the AdonisJs framework.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Li6dQAKMGTA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  You might also like
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/mezie/complete-authentication-in-adonisjs--2je4"&gt;Complete Authentication In AdonisJs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>adonisjs</category>
      <category>node</category>
    </item>
    <item>
      <title>Complete Authentication In AdonisJs</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Thu, 08 Feb 2018 18:02:35 +0000</pubDate>
      <link>https://forem.com/mezie/complete-authentication-in-adonisjs--2je4</link>
      <guid>https://forem.com/mezie/complete-authentication-in-adonisjs--2je4</guid>
      <description>&lt;p&gt;One of my goal this year is to do more video contents. So, I created a YouTube channel that will hold these videos &lt;a href="https://www.youtube.com/channel/UCbQd7u5BfaKCH1E4O5h-N7A" rel="noopener noreferrer"&gt;TutStack&lt;/a&gt;. In the first set of videos on the channel, I'm doing a series showing how to do authentication in AdonisJs 4.0:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/bI49nqFrm6o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I hope you enjoy and learn from these videos. I'll love to hear what you think about the videos, kindly drop your questions and feedback in the comment box below the videos.&lt;/p&gt;

&lt;p&gt;Also, please do subscribe to the channel so as to be notified once new videos are uploaded.&lt;/p&gt;

</description>
      <category>adonisjs</category>
    </item>
    <item>
      <title>How to build a public anonymous chat app using .NET and Pusher</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Mon, 15 Jan 2018 12:11:36 +0000</pubDate>
      <link>https://forem.com/mezie/how-to-build-a-public-anonymous-chat-app-using-net-and-pusher-pbj</link>
      <guid>https://forem.com/mezie/how-to-build-a-public-anonymous-chat-app-using-net-and-pusher-pbj</guid>
      <description>&lt;p&gt;Anonymity gives the likelihood to wear a cover, to end up being anyone you need to be. Also, anonymous communication permits you to quit being bashful and act naturally. It is a chance to pull in individuals whom you won't have the confidence to get to know, all things considered.&lt;/p&gt;

&lt;p&gt;Today, we will create a real-time public anonymous group chat app using C# ASP.NET and Pusher. This tutorial assumes the reader has basic knowledge of C# ASP.NET.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Pusher App
&lt;/h2&gt;

&lt;p&gt;We need to sign up on &lt;a href="https://pusher.com/signup" rel="noopener noreferrer"&gt;Pusher&lt;/a&gt; and create a new app, and also copy our secret, application key and application id.&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-setup-pusher.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-setup-pusher.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up The Asp.Net Project In Visual Studio
&lt;/h2&gt;

&lt;p&gt;We need to create a new Asp.Net MVC application, so we open up Visual Studio, select new project from the sidebar, under templates, select &lt;code&gt;Visual C#&lt;/code&gt;, next, select web, and finally in the middle section, select &lt;code&gt;ASP.NET Web Application&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-setup-visual_studio.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-setup-visual_studio.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are almost ready. The next step will be to install the official Pusher library for &lt;code&gt;.Net&lt;/code&gt; using the NuGet Package. To do this, we go to tools on the top bar, click on &lt;code&gt;NuGet Package Manager&lt;/code&gt;, on the dropdown we select &lt;code&gt;Package Manager Console&lt;/code&gt;. After doing this, we will see the &lt;code&gt;Package Manager Console&lt;/code&gt; at the bottom of our Visual Studio as shown below.&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-nuget-command.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-nuget-command.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to install the library, by running the following command in the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Install-Package PusherServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is done, our environment has now been set up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crafting The Chat Application
&lt;/h2&gt;

&lt;p&gt;Now that our environment is set up and ready, let us dive into writing some code.&lt;br&gt;
By default, Visual Studio creates three controllers for us, however we will be using the &lt;code&gt;HomeController&lt;/code&gt; for the logic of our chat application.&lt;br&gt;
The first thing we want to do is to delete the default &lt;code&gt;index.cshtml&lt;/code&gt; file under the &lt;code&gt;Views/Home&lt;/code&gt; folder, and create a new view file named &lt;code&gt;index.cshtml&lt;/code&gt; that does not have a master layout. In our new &lt;code&gt;index.cshtml&lt;/code&gt; file, let us copy the following contents into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @{
        Layout = null;
        Response.ContentType = "text/HTML";
    }
    &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;
            Pusher Tutorial
        &amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" /&amp;gt;
    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="//js.pusher.com/4.0/pusher.min.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
        .chat {
            list-style: none;
            margin: 0;
            padding: 0;
        }

            .chat li {
                margin-bottom: 10px;
                padding-bottom: 5px;
                border-bottom: 1px dotted #B3A9A9;
            }

                .chat li.left .chat-body {
                    margin-left: 60px;
                }

                .chat li.right .chat-body {
                    margin-right: 60px;
                }


                .chat li .chat-body p {
                    margin: 0;
                    color: #777777;
                }

            .panel .slidedown .glyphicon, .chat .glyphicon {
                margin-right: 5px;
            }

        .panel-body {
            overflow-y: scroll;
            height: 250px;
        }

        ::-webkit-scrollbar-track {
            -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
            background-color: #F5F5F5;
        }

        ::-webkit-scrollbar {
            width: 12px;
            background-color: #F5F5F5;
        }

        ::-webkit-scrollbar-thumb {
            -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
            background-color: #555;
        }
    &amp;lt;/style&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;div class="container"&amp;gt;
            &amp;lt;div class="row"&amp;gt;
                &amp;lt;div class="col-md-12"&amp;gt;
                    &amp;lt;div class="panel panel-primary"&amp;gt;
                        &amp;lt;div class="panel-heading"&amp;gt;
                            &amp;lt;span class="glyphicon glyphicon-comment"&amp;gt;&amp;lt;/span&amp;gt; Chat
                        &amp;lt;/div&amp;gt;
                        &amp;lt;div class="panel-body"&amp;gt;
                            &amp;lt;ul class="chat" id="chat"&amp;gt;&amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                        &amp;lt;div class="panel-footer"&amp;gt;
                            &amp;lt;div class="input-group"&amp;gt;
                                &amp;lt;input id="btn-input" class="form-control input-sm" placeholder="Type your message here..." type="text"&amp;gt;
                                &amp;lt;span class="input-group-btn"&amp;gt;
                                    &amp;lt;button class="btn btn-warning btn-sm" id="btn-chat"&amp;gt;
                                        Send
                                    &amp;lt;/button&amp;gt;
                                &amp;lt;/span&amp;gt;
                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;    
    &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above piece of code, we have defined the layout to be &lt;code&gt;null&lt;/code&gt;, and we have defined the content type as &lt;code&gt;"text/HTML"&lt;/code&gt; so Asp.Net does not attempt to parse the page as &lt;code&gt;XML&lt;/code&gt;. We required Bootstrap CSS, jQuery library, as well as the Pusher JavaScript library, before defining the HTML structure. If we save our file and run our project, we should see this.&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-builda-a-public-anonymous-chat-app-using-.NET-and-pusher-app-view.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-builda-a-public-anonymous-chat-app-using-.NET-and-pusher-app-view.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have to trigger an event when someone enters some text and clicks the send button. Let's open up our &lt;code&gt;index.cshtml&lt;/code&gt; file again and add the following at the end of the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;script&amp;gt;
        $(document).ready(function(){
            $("#btn-chat").click(function(){
                var message = $('#btn-input').val();

                $.post({
                    url: '@Url.Action("Pushermessage", "Home")',
                    dataType: 'text/HTML',
                    contentType: "application/json",
                    data: JSON.stringify({
                        "message": message
                    }),
                    success: function (data) {
                        $("#btn-input").val('');
                    }
                });
            })
        })
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we have attached a click event listener to the element with the ID of &lt;code&gt;btn-chat&lt;/code&gt; which happens to be our button. Once the button is clicked, the code will take the value of the element with the id of &lt;code&gt;btn-input&lt;/code&gt; which happens to be our text box, and send an AJAX call to our &lt;code&gt;Pushermessage&lt;/code&gt; function in our &lt;code&gt;HomeController&lt;/code&gt;. However, we are yet to create the &lt;code&gt;Pushermessage&lt;/code&gt; function that responds to the AJAX call.&lt;/p&gt;

&lt;p&gt;Let us move to our &lt;code&gt;HomeController&lt;/code&gt;, and paste the following code after the &lt;code&gt;index&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     [HttpPost]
            public async Task&amp;lt;ActionResult&amp;gt; Pushermessage(String message)
            {
                var options = new PusherOptions();
                options.Cluster = "XXX_CLUSTER";
                var pusher = new Pusher("XXX_APP_ID", "XXX_APP_KEY", "XXX_APP_SECRET", options);
                ITriggerResult result = await pusher.TriggerAsync("asp_channel", "asp_event", new { message = message, name = "Anonymous" });

                return new HttpStatusCodeResult((int)HttpStatusCode.OK);

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

&lt;/div&gt;



&lt;p&gt;Don't forget to add the following references to the top of your file, before the class declaration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    using PusherServer;
    using System.Net;
    using System.Threading.Tasks;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the last two block of codes, we have defined our &lt;code&gt;Pushermessage&lt;/code&gt; function and we also decorated it with the &lt;code&gt;[HttpPost]&lt;/code&gt; decorator, so Asp.Net knows it’s a function for POST requests. In this function, we instantiate Pusher, using our appId, appKey and appSecret respectively. We then went ahead to trigger a channel called &lt;code&gt;asp_channel&lt;/code&gt; and an event called &lt;code&gt;asp_event&lt;/code&gt;, sending in two values with it.&lt;/p&gt;

&lt;p&gt;At this point, if we reload our app, type in a message and send, we should see the following when we visit our debug console on our Pusher dashboard:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-pusher-debug.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-build-a-public-anonymous-chat-app-using-.NET-and-pusher-pusher-debug.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, we are done with emitting the message to data. Let us now move onto listening for the event on the client side and displaying the new message.&lt;/p&gt;

&lt;p&gt;Let us open up our &lt;code&gt;index.cshtml&lt;/code&gt; file, and add the following lines of code after our &lt;code&gt;click&lt;/code&gt; event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    var pusher = new Pusher('PUSHER_APP_KEY', {cluster: 'XXX_CLUSTER'});
    var my_channel = pusher.subscribe('asp_channel');
    my_channel.bind("asp_event", function (data) {
        var new_message = '&amp;lt;li class="left clearfix"&amp;gt;&amp;lt;span class="chat-img pull-left"&amp;gt;';
        new_message +='&amp;lt;img src="http://placehold.it/50/55C1E7/fff&amp;amp;text='+data.name+'" alt="User Avatar" class="img-circle"&amp;gt;';
        new_message +=  '&amp;lt;/span&amp;gt;';
        new_message +=      '&amp;lt;div class="chat-body clearfix"&amp;gt;';
        new_message +=          '&amp;lt;div class="header"&amp;gt;';
        new_message +=               '&amp;lt;strong class="primary-font"&amp;gt;'+data.name+'&amp;lt;/strong&amp;gt; &amp;lt;small class="pull-right text-muted"&amp;gt;';
        new_message +=                  '&amp;lt;/div&amp;gt;';
        new_message +=                      '&amp;lt;p&amp;gt;';
        new_message +=  data.message;
        new_message +=                      '&amp;lt;/p&amp;gt;';
        new_message +=                   '&amp;lt;/div&amp;gt;';
        new_message +=      '&amp;lt;/li&amp;gt;';
    $("#chat").append(new_message);
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above block of code, we declared a variable called &lt;code&gt;pusher&lt;/code&gt; and we set it to an instance of a new Pusher object, passing in our &lt;code&gt;appKey&lt;/code&gt;. Next, we declared a variable called &lt;code&gt;my_channel&lt;/code&gt;, and we call the Pusher subscribe method to our channel, which in this case, is called &lt;code&gt;asp_channel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we bind to the event, receive the data passed from it, wrap the data in some &lt;code&gt;li&lt;/code&gt; tags, and then we append it to the &lt;code&gt;ul&lt;/code&gt; element in our HTML structure with the ID of &lt;code&gt;chat&lt;/code&gt;. Below is our functionality:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-make-a-public-anonymous-chat-app-using-.NEt-and-pusher-view-demo.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F06%2Fhow-to-make-a-public-anonymous-chat-app-using-.NEt-and-pusher-view-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we have demonstrated how to create a public anonymous chat application using C# ASP.NET and Pusher. We have gone over the process of setting up the environment, using the NuGet Package Console to install packages as well as implementing the chat application.&lt;/p&gt;

&lt;p&gt;Many other real-time applications can be built using Pusher and ASP.NET, it's left for you to decide which awesome realtime app you'll be building next.&lt;/p&gt;

&lt;p&gt;This post was originally posted by the author on the &lt;a href="https://blog.pusher.com/build-public-anonymous-chat-app-using-net-pusher" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>net</category>
      <category>pusher</category>
    </item>
    <item>
      <title>How to build a public anonymous chat app in Android using Pusher</title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Mon, 15 Jan 2018 12:09:05 +0000</pubDate>
      <link>https://forem.com/mezie/how-to-build-a-public-anonymous-chat-app-in-android-using-pusher-377k</link>
      <guid>https://forem.com/mezie/how-to-build-a-public-anonymous-chat-app-in-android-using-pusher-377k</guid>
      <description>&lt;p&gt;Today, we will be learning how to build a public anonymous chatroom with Android.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will be using React Native to build our Android application. React Native lets you build mobile apps using only JavaScript. It uses the same design as React, letting you compose a rich mobile UI from declarative components. To learn more about React Native, please visit &lt;a href="https://facebook.github.io/react-native/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up React Native
&lt;/h2&gt;

&lt;p&gt;First, we need to install the React Native CLI if we don't already have it. To install React Native, we run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g react-native-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing the CLI, it's time to create our project. Open up a terminal, and create a new project called &lt;code&gt;pubchat&lt;/code&gt;using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native init pubchat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We wait until React Native does all its installations, then we can change directory into the new project and run the application using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//change directory to pubchat
cd pubchat
//run the application
react-native run-android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Please note, that before running the &lt;code&gt;run-android&lt;/code&gt; command, you should have an emulator running, or an Android device connected via &lt;code&gt;adb&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can read more on setting up React Native Android app from &lt;a href="https://facebook.github.io/react-native/docs/android-setup.html" rel="noopener noreferrer"&gt;https://facebook.github.io/react-native/docs/android-setup.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, we should see this kind of screen:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-first.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-first.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, you may run into an error like this:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-error.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-error.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix the error, all you need to do is to run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up Pusher
&lt;/h2&gt;

&lt;p&gt;At this point, React Native is ready and set up. We need to setup Pusher, as well as grab our app credentials.&lt;/p&gt;

&lt;p&gt;We need to sign up on &lt;a href="https://pusher.com/signup" rel="noopener noreferrer"&gt;Pusher&lt;/a&gt; and create a new app, and also copy our secret, application key and application id.&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-setup.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-setup.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then need to install the required libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install native-base pusher-js pusher express body-parser --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above bash command, we installed 4 packages. I will explain what the four packages do below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native-base: An essential cross-platform UI components for React Native. This helps us to reduce time writing and styling UI components ourselves.&lt;/li&gt;
&lt;li&gt;pusher-js: This is the official Pusher JavaScript client. We'll be using its React Native library to subscribe and listen to events in our application.&lt;/li&gt;
&lt;li&gt;pusher: This is the official Pusher library for Node.js. We will be using Node.js for our API, so this library will come in handy.&lt;/li&gt;
&lt;li&gt;express: This is a Node.js web framework which we'll use to create our API.&lt;/li&gt;
&lt;li&gt;body-parser: This library is used by Express to parse body requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After installing these packages, we need to link them with React Native, so we run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;react-native link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Please note that because we will be using Fetch to perform AJAX request, we would need to go to our android manifest located in &lt;code&gt;android/app/src/AndroidManifest.xml&lt;/code&gt; and add the following permission: *&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Our API
&lt;/h2&gt;

&lt;p&gt;First, let's create a new file called &lt;code&gt;server.js&lt;/code&gt;, which serves as our API in our root folder and place in the following contents into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// server.js

//require express
var express = require('express')
//define app as in instance of express
var app = express()
//require bosy-parser
var bodyParser = require('body-parser')
//require pusher
var Pusher = require('pusher')
//use bodyparser as a middle ware
app.use(bodyParser.json())
//instantiate pusher
const pusher = new Pusher({
  appId: 'XXX_APP_ID',
  key: 'XXX_APP_KEY',
  secret: 'XXX_APP_SECRET',
  cluster: 'XXX_APP_CLUSTER',
  encrypted: true
});
//set cors middleware
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});
//handle route postfunction
app.post('/', function (req, res) {
    pusher.trigger("pubchat", "message_sent", { message : req.body.message, name : "Anonymous" });
    res.send({
        message:'message_sent'
    });
})
//listen on port and serve the app
app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code block above is our Express server setup. At the beginning of the file, we had required Express, Body-parser and Pusher libraries for Node.js respectively. We had also initialized a new Pusher object, passing in our &lt;code&gt;appId&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt; to it, and we set the output of the object to a constant called &lt;code&gt;pusher&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we set the CORS header to our request, using a middleware function.&lt;/p&gt;

&lt;p&gt;Finally, we create a post handler for the &lt;code&gt;\&lt;/code&gt; route, and we then make a &lt;code&gt;Pusher&lt;/code&gt; trigger to a channel called &lt;code&gt;pubchat&lt;/code&gt; with an event called &lt;code&gt;message_sent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's take note of both the channel name and the event name used on this server. The channel name will be subscribed to, while we will listen for the event in our React Native app.&lt;/p&gt;

&lt;p&gt;This is all we need at the server side for our API call to work.&lt;/p&gt;

&lt;p&gt;Next, we go to our command line and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Crafting Up The Application
&lt;/h2&gt;

&lt;p&gt;Now let's replace our &lt;code&gt;index.android.js&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.android.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TextInput,
  ScrollView
} from 'react-native';
// import native base components
import { Container, Content, Footer, Button} from 'native-base';
//import pusher
import Pusher from 'pusher-js/react-native'
//react-native class
export default class pubchat extends Component {
//load constructor
constructor(props){
  super(props);
  //declare an array of messages
  var messages_array = [];
  // declare initial states
   this.state ={
    messages_array,
    text:'' 
   }

  //instantiate pusher
  var pusher = new Pusher('XXX_APP_KEY', {
    cluster: 'XXX_APP_CLUSTER'
  });
  //subscribe to the public chat channel
  var my_channel = pusher.subscribe('pubchat');
  //bind and listen for chat events
  my_channel.bind("message_sent", (data)=&amp;gt; {
     this.state.messages_array.push(data);
        this.setState({
          text:''
        })
  });
}

  //function that sends messahe
  send_message(){
    //check that the text input isnt empty
    if(this.state.text !=""){
      fetch('XXX_IP_TO_MY_ROUTE', {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          message: this.state.text
        })
      })
      .then((response) =&amp;gt; response.json()) 
      .then((responseJson) =&amp;gt; {}) 
      .catch((error) =&amp;gt; { console.error(error); });
    }
  }


  //function that loops over our messages and displays them
  loop(){
      var element = [];
     for (var index = 0; index &amp;lt; this.state.messages_array.length; index++) {

            element.push(&amp;lt;View key={"container"+index} &amp;gt;
                            &amp;lt;Text key = {"author"+index}&amp;gt;
                              {this.state.messages_array[index].name}
                            &amp;lt;/Text&amp;gt;
                            &amp;lt;Text key = {index} style={styles.bubble_you} &amp;gt;
                              {this.state.messages_array[index].message}
                            &amp;lt;/Text&amp;gt;
                        &amp;lt;/View&amp;gt;);
        }
         return element;
  };

  //render function that actually shows the page
  render() {
    //execute the loop function and store its response into a variable
    myloop = this.loop();

    return (
      &amp;lt;Container&amp;gt;
      &amp;lt;ScrollView &amp;gt;
        &amp;lt;View style={styles.container}&amp;gt;
          &amp;lt;Text style={styles.welcome}&amp;gt;
            Welcome to the public chat room!
          &amp;lt;/Text&amp;gt;
              {myloop}
        &amp;lt;/View&amp;gt;
        &amp;lt;/ScrollView&amp;gt;
        &amp;lt;Footer &amp;gt;
          &amp;lt;TextInput
            value ={this.state.text}
            style={{width: '80%'}}
            placeholder="Enter Your message!"
            onChangeText={(text) =&amp;gt; this.setState({text})}
          /&amp;gt;
          &amp;lt;Button onPress={()=&amp;gt; this.send_message()}&amp;gt;
            &amp;lt;Text&amp;gt;Send&amp;lt;/Text&amp;gt;
          &amp;lt;/Button&amp;gt; 
        &amp;lt;/Footer&amp;gt;
      &amp;lt;/Container&amp;gt;
    );
  }
}

//stylesheet 
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  bubble_you: {
    color: '#fff',
    backgroundColor: '#00b0ff',
  width: '50%',
  borderRadius: 25,
  padding: 7,
  marginBottom: 2,
  },
});

AppRegistry.registerComponent('pubchat', () =&amp;gt; pubchat);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we have imported the Native Base component to help us with our UI styling instead of the default React Native components. Next, we imported Pusher for React Native, then we declare our React Native class.&lt;/p&gt;

&lt;p&gt;We proceed by creating a constructor, and in our constructor, two states are declared namely: &lt;code&gt;messages_array&lt;/code&gt; and &lt;code&gt;text&lt;/code&gt;, which respectively represent our array of messages as well as the current text that is being typed.&lt;/p&gt;

&lt;p&gt;Next, we instantiate Pusher, passing in our &lt;code&gt;APP_KEY&lt;/code&gt;. Then we subscribe to the channel which we are emitting to from the server called &lt;code&gt;pubchat&lt;/code&gt; and also we listen to the &lt;code&gt;message_sent&lt;/code&gt; event which we also trigger from our server.&lt;/p&gt;

&lt;p&gt;While listening to the &lt;code&gt;message_sent&lt;/code&gt; event, we push the data that arrives at our &lt;code&gt;messages_array&lt;/code&gt; state, and also set our &lt;code&gt;text&lt;/code&gt; state to empty.&lt;/p&gt;

&lt;p&gt;Next, we create a function which sends our messages to the server, so it can be sent to Pusher. In this function, we first check if the state is empty, to avoid sending empty messages to the server.&lt;/p&gt;

&lt;p&gt;Next, we use the &lt;code&gt;fetch&lt;/code&gt; API provided by React Native to send an AJAX request to our server which we created earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: If you use IP address such as &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;localhost&lt;/code&gt;, the request is most likely going to fail. This is because, in React Native, &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; refers to the internal application. Please use the network IP for your system instead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we define a &lt;code&gt;loop&lt;/code&gt; function, which loops through all our messages and pushes them into an array which is being returned. This function would be used to display all messages on the UI.&lt;/p&gt;

&lt;p&gt;The next function is our &lt;code&gt;render&lt;/code&gt; function, which is a native React Native function. First, we declare a variable called &lt;code&gt;myloop&lt;/code&gt; and set it to our &lt;code&gt;loop&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;In our return statement, the &lt;code&gt;myloop&lt;/code&gt; variable was rendered, so it can display its content. Also, take a look at the &lt;code&gt;footer&lt;/code&gt; tag we have there. In the &lt;code&gt;footer&lt;/code&gt; tag, we have a &lt;code&gt;text input&lt;/code&gt; and a &lt;code&gt;button&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The text input text is used to set the &lt;code&gt;text&lt;/code&gt; state anytime the text changes using the &lt;code&gt;onChangeText&lt;/code&gt; event of the button. Notice that our button also calls the &lt;code&gt;send_message&lt;/code&gt; function anytime it is pressed by binding it to its &lt;code&gt;onPress&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Finally, we defined some style sheets.&lt;/p&gt;

&lt;p&gt;At this point if we reload our application, our app would look like the following:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-lookslike.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-lookslike.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, once our server is up and running, we should go to the application, type in a message, then send.&lt;/p&gt;

&lt;p&gt;Here is a demo of what we have built:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-demo.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F04%2Fbuild-public-anonymous-chat-app-android-pusher-demo.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The complete source code is available on &lt;a href="https://github.com/ammezie/android-pubchat" rel="noopener noreferrer"&gt;Github&lt;/a&gt; for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we have demonstrated how to make a public anonymous chat application in Android using React Native. We have secured the design choices which are important, to begin with, and the cases above ought to help you fill in the holes and give an outline of a portion of the other design choices accessible to you.&lt;/p&gt;

&lt;p&gt;How well has this tutorial helped you to get started with Pusher and Android apps? Do you have other use cases or scenarios you might want to talk about? Let us know in the comments.&lt;/p&gt;

&lt;p&gt;This post was originally posted by the author on the &lt;a href="https://blog.pusher.com/build-public-anonymous-chat-app-android-pusher" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>pusher</category>
    </item>
    <item>
      <title>How to build a ‘who’s typing’ feature with Laravel and Pusher </title>
      <dc:creator>Chimezie Enyinnaya</dc:creator>
      <pubDate>Mon, 15 Jan 2018 12:04:22 +0000</pubDate>
      <link>https://forem.com/mezie/how-to-build-a-whos-typing-feature-with-laravel-and-pusher-2dkk</link>
      <guid>https://forem.com/mezie/how-to-build-a-whos-typing-feature-with-laravel-and-pusher-2dkk</guid>
      <description>&lt;p&gt;When you're using a chat app, having a 'who's typing' feature really improves the user experience and makes it easier for users to interact with the application. If you're chatting with someone, you can easily see when the other person is responding to your chats, saving you time from typing &lt;strong&gt;'you there?'&lt;/strong&gt; and let you know when to wait for the other person's response, so you don't keep sending messages.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will show you how to build a 'who's typing' feature with Laravel and Pusher, using the concept of a chat app to demonstrate it. Note that this tutorial focuses on the 'who's typing' part of the app (if you want to learn how to build a chat app using Laravel and Pusher, you should read &lt;a href="https://blog.pusher.com/how-to-build-a-laravel-chat-app-with-pusher/" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let's take a quick look at what we'll be building:&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F03%2Fwhos-typing-laravel-pusher.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F03%2Fwhos-typing-laravel-pusher.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code of the completed demo is available on &lt;a href="https://github.com/ammezie/laravel-whos-typing" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Laravel
&lt;/h2&gt;

&lt;p&gt;Create a new Laravel project. (I prefer using the Laravel installer) Open your terminal and run the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;laravel new laravel-whos-typing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to setup our new Laravel project. First, we need to register the &lt;code&gt;App\Providers\BroadcastServiceProvider&lt;/code&gt;. Open &lt;code&gt;config/app.php&lt;/code&gt; and uncomment &lt;code&gt;App\Providers\BroadcastServiceProvider&lt;/code&gt; in the &lt;code&gt;providers&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;We then need to tell Laravel that we are using the Pusher driver in the &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env

BROADCAST_DRIVER=pusher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we specified we want to use Pusher as our broadcasting driver, we need to install the Pusher PHP SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require pusher/pusher-php-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up Pusher
&lt;/h2&gt;

&lt;p&gt;If you don't have one already, create a free Pusher account at &lt;a href="https://pusher.com/signup" rel="noopener noreferrer"&gt;https://pusher.com/signup&lt;/a&gt; then log in to your dashboard and create an app. Take note of your app credentials as we'll be using them shortly.&lt;/p&gt;

&lt;p&gt;Now, let's fill in our Pusher app credentials. If you open the &lt;code&gt;config/broadcasting.php&lt;/code&gt;, you'll notice that Laravel is pulling some of the Pusher credentials from the &lt;code&gt;.env&lt;/code&gt; file. So let's update the &lt;code&gt;.env&lt;/code&gt; file to contain our Pusher app credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env

PUSHER_APP_ID=xxxxxx
PUSHER_APP_KEY=xxxxxxxxxxxxxxxxxxxx
PUSHER_APP_SECRET=xxxxxxxxxxxxxxxxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to replace the &lt;code&gt;x&lt;/code&gt;s with your Pusher app credentials. You can find your app credentials under the &lt;strong&gt;Keys&lt;/strong&gt; section on the &lt;strong&gt;Overview&lt;/strong&gt; tab in the Pusher Dashboard.&lt;/p&gt;

&lt;p&gt;Also, remember to fill in the &lt;code&gt;cluster&lt;/code&gt; of your Pusher app and other additional options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// config/broadcasting.php

'options' =&amp;gt; [
   'cluster' =&amp;gt; 'eu',
   'encrypted' =&amp;gt; true
],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Frontend Dependencies
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we'll be using &lt;code&gt;Bootstrap&lt;/code&gt;, &lt;code&gt;Vue&lt;/code&gt; and &lt;code&gt;Axios&lt;/code&gt;, which have been setup for us by Laravel, though we still need to install each of the dependencies. To compile our CSS and JavaScript, we need to install Laravel Mix, which is a wrapper around Webpack. We can install these dependencies through &lt;code&gt;NPM&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to install Laravel Echo, which is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel and of course the Pusher JavaScript library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save laravel-echo pusher-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, we need to tell Laravel Echo to use Pusher. At the bottom of the &lt;code&gt;resources/assets/js/bootstrap.js&lt;/code&gt; file, uncomment the Laravel Echo section and update the details with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// resources/assets/js/bootstrap.js

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: Laravel.pusherKey,
    cluster: 'eu',
    encrypted: true
}); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we specified our Pusher app credentials in the &lt;code&gt;.env&lt;/code&gt; file, notice &lt;code&gt;Laravel.pusherKey&lt;/code&gt; from the code above, we'll load our Pusher app key from the config instead of hard coding it directly in &lt;code&gt;resources/assets/js/bootstrap.js&lt;/code&gt;. We'll define &lt;code&gt;Laravel.pusherKey&lt;/code&gt; in a later section.&lt;/p&gt;

&lt;p&gt;Also use the same &lt;code&gt;cluster&lt;/code&gt; that you specified earlier in &lt;code&gt;config/broadcasting.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the setups done, let's start implementing a 'who's typing' feature in our Laravel application.&lt;/p&gt;

&lt;p&gt;To do this, we need to know when a user is typing into the chat box. There are different ways to accomplish this, but in this tutorial, we'll add event listeners to the chat box. With this, we can know when a user is actually typing a message and display the typing indicator to the appropriate users.&lt;/p&gt;

&lt;p&gt;We'll make use of JavaScript keyboard events: &lt;code&gt;onkeydown&lt;/code&gt; which fires event when a user is pressing a key. This means that when a user presses a key, we send events to Pusher. These types of events are called &lt;code&gt;client events&lt;/code&gt; and do not hit the server at all. But how do we broadcast such events to Pusher since they are a bit different from the normal events (server to Pusher events)? Well, Pusher is aware of such events and has a special way of handling them.&lt;/p&gt;

&lt;p&gt;By default, when you create a Pusher app, client events are not enabled. We have to enable this for our app. To enable client events in your Pusher app, select the app then click on the &lt;strong&gt;App Settings&lt;/strong&gt; tab and then check the box next to &lt;strong&gt;Enable client events&lt;/strong&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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F03%2Fwhos-typing-laravel-pusher-enable-client-event.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/http%3A%2F%2Fblog.pusher.com%2Fwp-content%2Fuploads%2F2017%2F03%2Fwhos-typing-laravel-pusher-enable-client-event.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we have enabled client events in our Pusher app, we can now trigger and listen for client events in our chat app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticating Users
&lt;/h2&gt;

&lt;p&gt;Our chat app will require users to be logged in before they can begin to chat. So, we need an authentication system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan make:auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the necessary routes, views and controllers needed for an authentication system.&lt;/p&gt;

&lt;p&gt;Before we go on to create users, we need to run the &lt;code&gt;users&lt;/code&gt; migration that comes with a fresh installation of Laravel. But to do this, we first need to setup our database. Open the &lt;code&gt;.env&lt;/code&gt; file and enter your database details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel-chat
DB_USERNAME=root
DB_PASSWORD=root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update with your own database details. Now, we can run our migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: There's a bug in Laravel 5.4 if you're running a version of MySQL older than 5.7.7 or MariaDB older than 10.2.2. More info &lt;a href="https://github.com/laravel/framework/issues/17508" rel="noopener noreferrer"&gt;here&lt;/a&gt;. This can be fixed by replacing the &lt;code&gt;boot()&lt;/code&gt; of &lt;code&gt;app/Providers/AppServiceProvider.php&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Providers/AppServiceProvider.php

// remember to use
Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
  Schema::defaultStringLength(191);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining App Routes
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;routes/web.php&lt;/code&gt; and replace the routes with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// routes/web.php

Auth::routes();

Route::get('/', function () {
    return view('chat');
})-&amp;gt;middleware('auth');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The routes are pretty simple: a route that will handle authentication and a route to the homepage that will render a chat view which we'll create shortly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Since we have removed the &lt;code&gt;/home&lt;/code&gt; route, you might want to update the &lt;code&gt;redirectTo&lt;/code&gt; property of both &lt;code&gt;app/Http/Controllers/Auth/LoginController.php&lt;/code&gt; and &lt;code&gt;app/Http/Controllers/Auth/RegisterController.php&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected $redirectTo = '/';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating The Chat App View
&lt;/h2&gt;

&lt;p&gt;Create a new &lt;code&gt;resources/views/chat.blade.php&lt;/code&gt; file and paste into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// resources/views/chat.blade.php

@extends('layouts.app')

@section('content')
&amp;lt;div class="container"&amp;gt;
    &amp;lt;div class="row"&amp;gt;
        &amp;lt;div class="col-md-8 col-md-offset-2"&amp;gt;
            &amp;lt;div class="panel panel-default"&amp;gt;
                &amp;lt;div class="panel-heading"&amp;gt;Chats&amp;lt;/div&amp;gt;

                &amp;lt;div class="panel-body"&amp;gt;
                    &amp;lt;ul&amp;gt;
                        &amp;lt;li v-for="message in messages"&amp;gt;
                            @{{ message.user.name }} - @{{ message.message }}
                        &amp;lt;/li&amp;gt;
                    &amp;lt;/ul&amp;gt;
                    &amp;lt;div&amp;gt;
                        &amp;lt;div class="input-group"&amp;gt;
                            &amp;lt;input type="text" name="message" class="form-control" placeholder="Type your message here..." v-model="newMessage" @keyup.enter="sendMessage"&amp;gt;
                            &amp;lt;span class="input-group-btn"&amp;gt;
                                &amp;lt;button class="btn btn-primary" @click="sendMessage"&amp;gt;
                                    Send
                                &amp;lt;/button&amp;gt;
                            &amp;lt;/span&amp;gt;
                        &amp;lt;/div&amp;gt;
                        &amp;lt;span v-show="typing" class="help-block" style="font-style: italic;"&amp;gt;
                            @{{ user }} is typing...
                        &amp;lt;/span&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
@endsection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again pretty straightforward, we are using Vue here. We loop through each of the messages and display them. Next, there is a input field and a send button for composing chat messages. The input field is binded to the &lt;code&gt;newMessage&lt;/code&gt; data. When the send button is clicked or the enter key is pressed on the input field, a &lt;code&gt;sendMessage()&lt;/code&gt; is called. Lastly, there is span holding the 'is typing' indicator. This will be hidden by default and will be displayed using Vue's &lt;code&gt;v-show&lt;/code&gt; when &lt;code&gt;typing&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; (that is when a user is typing).&lt;/p&gt;

&lt;p&gt;Notice that we are displaying the name of the user along with the 'is typing' indicator, we need a way to pass the authenticated user to our JavaScript file. Remember from &lt;code&gt;resources/assets/js/bootstrap.js&lt;/code&gt;, where we used &lt;code&gt;Laravel.pusherKey&lt;/code&gt;, we also need to pass Pusher app key to our JavaScript file. We can do this by updating the `&lt;code&gt;section in&lt;/code&gt;resources/views/layouts/app.blade.php&lt;code&gt;(which was created when we ran&lt;/code&gt;make:auth`) with:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-js&lt;br&gt;
resources/views/layouts/app.blade.php&lt;/p&gt;

&lt;p&gt;window.Laravel = {!! json_encode([&lt;br&gt;
    'csrfToken' =&amp;gt; csrf_token(),&lt;br&gt;
    'user' =&amp;gt; Auth::user(),&lt;br&gt;
    'pusherKey' =&amp;gt; config('broadcasting.connections.pusher.key'),&lt;br&gt;
  ]) !!};&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
The code above creates a&lt;/code&gt;Laravel&lt;code&gt;object on the global window, we then add some items to the object. The&lt;/code&gt;user&lt;code&gt;item will be the currently authenticated user and the&lt;/code&gt;pusherKey&lt;code&gt;item will load our Pusher app key from the&lt;/code&gt;config/broadcasting.php` config file.&lt;/p&gt;

&lt;p&gt;Laravel has integrated Vue and Axios for us, so we can start using &lt;code&gt;Vue&lt;/code&gt; without any further setup. There is an &lt;code&gt;app.js&lt;/code&gt;file that Laravel creates by default within &lt;code&gt;resources/assets/js/app.js&lt;/code&gt;. Open this file and update with the code below:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-js&lt;br&gt;
// resources/assets/js/app.js&lt;/p&gt;

&lt;p&gt;require('./bootstrap');&lt;/p&gt;

&lt;p&gt;const app = new Vue({&lt;br&gt;
    el: '#app',&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data: {
    messages: [],
    newMessage: '',
    user: '',
    typing: false
},

methods: {
    sendMessage() {
        // add new message to messages array
        this.messages.push({
            user: Laravel.user,
            message: this.newMessage
        });

        // clear input field
        this.newMessage = '';

        // persist to database
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
First, we require the&lt;/code&gt;resources/assets/js/bootsrap.js&lt;code&gt;file that contains our package setups and integrations. Next, we create a new Vue instance and bind it to **app**&lt;/code&gt;id&lt;code&gt;. We then create some data variables:&lt;/code&gt;messages&lt;code&gt;array will hold our chat messages,&lt;/code&gt;newMessage&lt;code&gt;will be the new message that a user sends,&lt;/code&gt;user&lt;code&gt;will be the currently authenticated user and finally&lt;/code&gt;typing&lt;code&gt;will hold&lt;/code&gt;true&lt;code&gt;or&lt;/code&gt;false` indicating whether a user is typing or not.&lt;/p&gt;

&lt;p&gt;Next we define a &lt;code&gt;sendMessage()&lt;/code&gt; that simply adds the new message along with the user that sent it to the messages array and finally clears the input field. (If you are building an actual chat app, you might want to do an AJAX request to persist the new message to the database here.)&lt;/p&gt;

&lt;p&gt;Having done this, we can start sending messages and our messages will be displayed on the chat view. Now let's move on to the meat of this tutorial; adding 'who's typing' to our Laravel application.&lt;/p&gt;

&lt;p&gt;Laravel Echo provides some handy methods to integrate with client events which we'll be using to implement our 'who's typing' indicator.&lt;/p&gt;

&lt;p&gt;Paste the code below into the &lt;code&gt;resources/assets/js/app.js&lt;/code&gt; within the &lt;code&gt;methods&lt;/code&gt; object:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-js&lt;br&gt;
// resources/assets/js/app.js&lt;/p&gt;

&lt;p&gt;isTyping() {&lt;br&gt;
  let channel = Echo.private('chat');&lt;/p&gt;

&lt;p&gt;setTimeout(function() {&lt;br&gt;
    channel.whisper('typing', {&lt;br&gt;
      user: Laravel.user,&lt;br&gt;
        typing: true&lt;br&gt;
    });&lt;br&gt;
  }, 300);&lt;br&gt;
},&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
The&lt;/code&gt;isTyping()&lt;code&gt;will be triggered when an **onkeydown** event is fired within the chat input field (that is, when a user is typing a message). First, we subscribe to a private channel called&lt;/code&gt;chat&lt;code&gt;and trigger a client event using Laravel Echo's&lt;/code&gt;whisper()&lt;code&gt;after 0.3s. The&lt;/code&gt;whisper()&lt;code&gt;accepts the name of the client event, in our case&lt;/code&gt;typing&lt;code&gt;and the data we want to broadcast. Since Pusher specifies that client events must be prefixed by&lt;/code&gt;client-&lt;code&gt;, Laravel is smart enough to prefix the&lt;/code&gt;client-&lt;code&gt;for us under the hood. We pass the user that is typing a message and typing as&lt;/code&gt;true` as the data we are broadcasting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Client events can only be triggered on private and presence channels because they require authentication. Also, client events are not delivered to the originator of the event. For more information on client events, kindly checkout the Pusher &lt;a href="https://pusher.com/docs/client_api_guide/client_events#trigger-events" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since we created a private channel, only authenticated users will be able to listen on the &lt;code&gt;chat&lt;/code&gt; channel. We need a way to authorize that the currently authenticated user can actually listen on the channel. This can be done by in the &lt;code&gt;routes/channels.php&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-php&lt;br&gt;
// routes/channels.php&lt;/p&gt;

&lt;p&gt;Broadcast::channel('chat', function ($user) {&lt;br&gt;
  return Auth::check();&lt;br&gt;
});&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
We pass to the&lt;/code&gt;channel(),&lt;code&gt;the name of our channel and a callback function that will either return&lt;/code&gt;true&lt;code&gt;or&lt;/code&gt;false` depending on whether the current user is authenticated.&lt;/p&gt;

&lt;p&gt;Now that we can trigger client events, we also need a way to listen for the client events in our application. To do this, add the code below to &lt;code&gt;resources/assets/js/app.js&lt;/code&gt; just after the &lt;code&gt;data&lt;/code&gt; object:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-js&lt;br&gt;
// resources/assets/js/app.js&lt;/p&gt;

&lt;p&gt;created() {&lt;br&gt;
  let _this = this;&lt;/p&gt;

&lt;p&gt;Echo.private('chat')&lt;br&gt;
    .listenForWhisper('typing', (e) =&amp;gt; {&lt;br&gt;
      this.user = e.user;&lt;br&gt;
      this.typing = e.typing;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // remove is typing indicator after 0.9s
  setTimeout(function() {
    _this.typing = false
  }, 900);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;},&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
Again we subscribe to the&lt;/code&gt;chat&lt;code&gt;channel. To listen for client events, we use the&lt;/code&gt;listenForWhisper()&lt;code&gt;and set both the&lt;/code&gt;user&lt;code&gt;and&lt;/code&gt;typing` data accordingly. Lastly, we remove the is typing indicator after 0.9s of a user not typing.&lt;/p&gt;

&lt;p&gt;Before we start testing the who's typing feature, let update the input field of the &lt;code&gt;chat&lt;/code&gt; view with:&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt; language-html&lt;br&gt;
// resources/view/chat.blade.php&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
We added two keyboard events:&lt;/code&gt;@keydown&lt;code&gt;and&lt;/code&gt;@keyup&lt;code&gt;which are Vue equivalent of the JavaScript keyboard events we talked about earlier. On keydown, the&lt;/code&gt;isTyping()&lt;code&gt;will be triggered and on keyup, the&lt;/code&gt;notyping()` will be triggered.&lt;/p&gt;

&lt;p&gt;We can now compile the JavaScript files using Laravel Mix using:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;` language-bash&lt;br&gt;
npm run dev&lt;br&gt;
`&lt;/code&gt;&lt;br&gt;
Now we can start our chat app by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;` language-bash&lt;br&gt;
php artisan serve&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With such a basic app, we have seen how to implement a 'who's typing' feature in a Laravel application using Pusher. I hope you find this tutorial helpful and if you encounter any problems following this tutorial, kindly drop a comment below and we'd tackle them together.&lt;/p&gt;

&lt;p&gt;This post was originally posted by the author on the &lt;a href="https://blog.pusher.com/build-whos-typing-feature-laravel-pusher" rel="noopener noreferrer"&gt;Pusher blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>vue</category>
      <category>pusher</category>
    </item>
  </channel>
</rss>
