<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mhcrocky</title>
    <description>The latest articles on DEV Community by mhcrocky (@mhcrocky).</description>
    <link>https://dev.to/mhcrocky</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%2F501633%2F32c5ec64-edc8-4e5e-8a55-1fe0c24a987a.jpg</url>
      <title>DEV Community: mhcrocky</title>
      <link>https://dev.to/mhcrocky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mhcrocky"/>
    <language>en</language>
    <item>
      <title>Building a CRUD Application with Ruby on Rails: Step-by-Step Guide</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Tue, 30 Jul 2024 00:10:54 +0000</pubDate>
      <link>https://dev.to/mhcrocky/building-a-crud-application-with-ruby-on-rails-step-by-step-guide-5e8d</link>
      <guid>https://dev.to/mhcrocky/building-a-crud-application-with-ruby-on-rails-step-by-step-guide-5e8d</guid>
      <description>&lt;p&gt;Let’s take a deep dive into building a fully functional CRUD (Create, Read, Update, Delete) application using Ruby on Rails. I will walk through each step of the process, providing technical examples and explanations to help you understand the concepts and implementation details. By the end of this guide, you will have a solid foundation in &lt;strong&gt;developing CRUD applications with Ruby on Rails.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Ruby on Rails&lt;/strong&gt;&lt;br&gt;
Make sure you have Ruby and Rails installed on your system. You can install Rails 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;gem install rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create a New Rails Application&lt;/strong&gt;&lt;br&gt;
Create a new Rails 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;rails new crud_app
cd crud_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Generate a Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generate a model for the application. In this example, we’ll create a &lt;code&gt;Task&lt;/code&gt; model with a title and description.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate model Task title:string description:text
rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Generate a Controller&lt;/strong&gt;&lt;br&gt;
Generate a controller to handle the CRUD operations for the Task model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails generate controller Tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Define Routes&lt;/strong&gt;&lt;br&gt;
Open the &lt;code&gt;config/routes.rb&lt;/code&gt; file and define the routes for the Tasks controller:&lt;/p&gt;
&lt;h1&gt;
  
  
  config/routes.rb
&lt;/h1&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.draw do
resources :tasks
root 'tasks#index'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 6: Implement Controller Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app/controllers/tasks_controller.rb&lt;/code&gt; file and implement the CRUD actions:&lt;/p&gt;
&lt;h1&gt;
  
  
  app/controllers/tasks_controller.rb
&lt;/h1&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class TasksController &amp;lt; ApplicationController
    before_action :set_task, only: [:show, :edit, :update, :destroy]
    def index
        @tasks = Task.all
    end

    def show
    end

    def new
        @task = Task.new
    end

    def create
        @task = Task.new(task_params)
        if @task.save
            redirect_to @task, notice: 'Task was successfully created.'
        else
            render :new
        end
    end

    def edit
    end

    def update
        if @task.update(task_params)
            redirect_to @task, notice: 'Task was successfully updated.'
        else
            render :edit
        end
    end

    def destroy
        @task.destroy
        redirect_to tasks_url, notice: 'Task was successfully destroyed.'
    end

    private
    def set_task
        @task = Task.find(params[:id])
    end

    def task_params
        params.require(:task).permit(:title, :description)
    end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 7: Create Views&lt;/strong&gt;&lt;br&gt;
Create views for the Tasks controller. You can use the default Rails scaffold views or customize them based on your needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8: Run the Application&lt;/strong&gt;&lt;br&gt;
Start the Rails server and navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt; to see your CRUD application in action.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;That’s it! You’ve now created a simple CRUD application with Ruby on Rails. You can further customize and enhance it based on your requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thanks for reading&lt;/strong&gt;&lt;br&gt;
Want to work together? Contact me on &lt;a href="https://t.me/mhcrocky" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am an freelancer have much much experience in React.js. Want to catch the journey? Follow me on &lt;a href="https://github.com/mhcrocky" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>learning</category>
    </item>
    <item>
      <title>Building a Text-to-Speech Avatar App with ReactJS and Azure TTS Avatar AI.</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Mon, 29 Jul 2024 08:43:40 +0000</pubDate>
      <link>https://dev.to/mhcrocky/building-a-text-to-speech-avatar-app-with-reactjs-and-azure-tts-avatar-ai-part-1st-1gia</link>
      <guid>https://dev.to/mhcrocky/building-a-text-to-speech-avatar-app-with-reactjs-and-azure-tts-avatar-ai-part-1st-1gia</guid>
      <description>&lt;p&gt;Have you ever imagined bringing your applications to life with talking avatars? In this tutorial, we’ll walk through the process of creating a Text-to-Speech (TTS) avatar application using ReactJS and Azure AI. This engaging feature converts text into a digital video, featuring a photorealistic human speaking with a natural-sounding voice. Whether you’re a seasoned developer or just starting, follow along to empower your applications with lifelike synthetic talking avatars.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Features of Text-to-Speech Avatar:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Flexible Voice Selection:

&lt;ul&gt;
&lt;li&gt;Choose from a range of prebuilt voices or even use a custom neural voice of your choice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Language Support:

&lt;ul&gt;
&lt;li&gt;Enjoy the same language support as Text-to-Speech, opening doors to a global audience.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Video Output Specifications:
Both batch 

&lt;ul&gt;
&lt;li&gt;and real-time synthesis offer a resolution of 1920 x 1080 with 25 frames per second (FPS).&lt;/li&gt;
&lt;li&gt;Codec options include h264 or h265 for batch synthesis in mp4 format, and vp9 for webm format.&lt;/li&gt;
&lt;li&gt;Real-time synthesis codec is h264, with configurable video bitrate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prebuilt avatars:

&lt;ul&gt;
&lt;li&gt;Provides a collection of prebuilt avatars.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Content creation without code:

&lt;ul&gt;
&lt;li&gt;Provides a &lt;a href="https://speech.microsoft.com/portal/talkingavatar" rel="noopener noreferrer"&gt;content creation tool&lt;/a&gt; in Speech Studio for creating video content without coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Custom Avatars:

&lt;ul&gt;
&lt;li&gt;Custom text to speech avatar allows you to create a customized, one-of-a-kind synthetic talking avatar for your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before diving into the code, make sure you have Node.js version 16.13.2 installed on your machine and a basic understanding of ReactJS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Step 1. Creating a relay token&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We need to get a relay token which will be used in Azure Avatar API. Following is the sample code on how to get a relay token:&lt;/p&gt;

&lt;p&gt;Go to azure portal and create a communication resource &lt;a href="https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech-avatar/real-time-synthesis-avatar" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech-avatar/real-time-synthesis-avatar&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;a href="https://portal.azure.com/#create/Microsoft.Communication" rel="noopener noreferrer"&gt;Communication resource&lt;/a&gt; in the Azure portal (for real-time avatar synthesis only).&lt;/p&gt;

&lt;p&gt;Here is an example of connection string :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://avatarcommnl.unitedstates.communication.azure.com/;accesskey=aowjrfymernticjnrng+fgXOt+sdffi0sdfsdfnhderunvngfgasd==
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: You need to put the entire string within double quotes and assign it to a variable.&lt;/p&gt;

&lt;p&gt;After creating the communication resource, initialize it to a variable and run the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { CommunicationIdentityClient } = require("@azure/communication-identity");
const { CommunicationRelayClient } = require("@azure/communication-network-traversal");;

const main = async () =&amp;gt; {
  console.log("Azure Communication Services - Relay Token Quickstart")

  const connectionString = "YOUR CONNECTION STRING"
 // Instantiate the identity client
  const identityClient = new CommunicationIdentityClient(connectionString);

  let identityResponse = await identityClient.createUser();
  console.log(`\nCreated an identity with ID: ${identityResponse.communicationUserId}`);

  const relayClient = new CommunicationRelayClient(connectionString);
  console.log("Getting relay configuration");

  const config = await relayClient.getRelayConfiguration(identityResponse);
  console.log("RelayConfig", config);

  console.log("Printing entire thing ",config.iceServers);  
};

main().catch((error) =&amp;gt; {
  console.log("Encountered and error");
  console.log(error);
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will get relay config printed on your console. Pickup username and credential from the relay config and save it somewhere, We will have to use this in our TTS application.&lt;/p&gt;

&lt;p&gt;Sample relay config data :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; {
    urls: [
      'stun:relay.communication.microsoft.com:3478',
      'turn:relay.communication.microsoft.com:3478'
    ],
    username: '--------------------------------',
    credential: '---------------------------------',
    routeType: 'any'
  },
  {
    urls: [
      'stun:relay.communication.microsoft.com:3478',
      'turn:relay.communication.microsoft.com:3478'
    ],
    username: '----------------------------------------------',
    credential: '-----------------------------------------',
    routeType: 'nearest'
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2. Initialize a ReactJS Application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a react application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app azure-avatar-demo
cd azure-avatar-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add following dev dependencies to your package.json file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "devDependencies": {
    "bootstrap": "^5.3.2",
    "microsoft-cognitiveservices-speech-sdk": "^1.33.1"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies&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;Basically we have installed all the required dependencies now we can create simple interface which will render the Azure AI Avatar&lt;/p&gt;

&lt;p&gt;In src directory create a directory called components. In components directory create a file Utility.js&lt;/p&gt;

&lt;p&gt;Now add the following code to Utility.js.&lt;br&gt;
&lt;/p&gt;

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

import * as SpeechSDK from "microsoft-cognitiveservices-speech-sdk";
import { avatarAppConfig } from "./config";
const cogSvcRegion = avatarAppConfig.cogSvcRegion
const cogSvcSubKey = avatarAppConfig.cogSvcSubKey
const voiceName = avatarAppConfig.voiceName
const avatarCharacter = avatarAppConfig.avatarCharacter
const avatarStyle = avatarAppConfig.avatarStyle
const avatarBackgroundColor = "#FFFFFFFF";


export const createWebRTCConnection = (iceServerUrl, iceServerUsername, iceServerCredential) =&amp;gt; {

    var peerConnection = new RTCPeerConnection({
        iceServers: [{
            urls: [ iceServerUrl ],
            username: iceServerUsername,
            credential: iceServerCredential
        }]
    })

    return peerConnection;

}

export const createAvatarSynthesizer = () =&amp;gt; {

    const speechSynthesisConfig = SpeechSDK.SpeechConfig.fromSubscription(cogSvcSubKey, cogSvcRegion)

    speechSynthesisConfig.speechSynthesisVoiceName = voiceName;

    const videoFormat = new SpeechSDK.AvatarVideoFormat()

    let videoCropTopLeftX =  600
    let videoCropBottomRightX = 1320
    videoFormat.setCropRange(new SpeechSDK.Coordinate(videoCropTopLeftX, 50), new SpeechSDK.Coordinate(videoCropBottomRightX, 1080));


    const talkingAvatarCharacter = avatarCharacter
    const talkingAvatarStyle = avatarStyle

    const avatarConfig = new SpeechSDK.AvatarConfig(talkingAvatarCharacter, talkingAvatarStyle, videoFormat)
    avatarConfig.backgroundColor = avatarBackgroundColor;
    let avatarSynthesizer = new SpeechSDK.AvatarSynthesizer(speechSynthesisConfig, avatarConfig)

    avatarSynthesizer.avatarEventReceived = function (s, e) {
        var offsetMessage = ", offset from session start: " + e.offset / 10000 + "ms."
        if (e.offset === 0) {
            offsetMessage = ""
        }
        console.log("[" + (new Date()).toISOString() + "] Event received: " + e.description + offsetMessage)
    }

    return avatarSynthesizer;

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

&lt;/div&gt;



&lt;p&gt;Let’s go through the code first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const createWebRTCConnection = (iceServerUrl, iceServerUsername, iceServerCredential) =&amp;gt; {
    var peerConnection = new RTCPeerConnection({
        iceServers: [{
            urls: [iceServerUrl],
            username: iceServerUsername,
            credential: iceServerCredential
        }]
    });

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

&lt;/div&gt;



&lt;p&gt;This function is responsible for creating and configuring a WebRTC (Real-Time Communication) connection. WebRTC is commonly used for peer-to-peer communication in real-time applications. Here’s a breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;iceServerUrl&lt;/code&gt;: The URL of the Interactive Connectivity Establishment (ICE) server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iceServerUsername&lt;/code&gt;: The username for the ICE server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iceServerCredential&lt;/code&gt;: The credential for the ICE server.&lt;/li&gt;
&lt;li&gt;Initializes a new &lt;code&gt;RTCPeerConnection&lt;/code&gt;object, representing a WebRTC connection.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;iceServers&lt;/code&gt;property is configured with the provided ICE server details.&lt;/li&gt;
&lt;li&gt;Returns the configured &lt;code&gt;peerConnection&lt;/code&gt;object.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const createAvatarSynthesizer = () =&amp;gt; {
    // Configuring Speech SDK for Speech Synthesis
    const speechSynthesisConfig = SpeechSDK.SpeechConfig.fromSubscription(cogSvcSubKey, cogSvcRegion);
    speechSynthesisConfig.speechSynthesisVoiceName = voiceName;

    // Configuring Avatar Video Format
    const videoFormat = new SpeechSDK.AvatarVideoFormat();
    let videoCropTopLeftX = 600;
    let videoCropBottomRightX = 1320;
    videoFormat.setCropRange(new SpeechSDK.Coordinate(videoCropTopLeftX, 50), new SpeechSDK.Coordinate(videoCropBottomRightX, 1080));

    // Avatar Configuration
    const talkingAvatarCharacter = avatarCharacter;
    const talkingAvatarStyle = avatarStyle;
    const avatarConfig = new SpeechSDK.AvatarConfig(talkingAvatarCharacter, talkingAvatarStyle, videoFormat);
    avatarConfig.backgroundColor = avatarBackgroundColor;

    // Creating Avatar Synthesizer
    let avatarSynthesizer = new SpeechSDK.AvatarSynthesizer(speechSynthesisConfig, avatarConfig);

    // Handling Avatar Events
    avatarSynthesizer.avatarEventReceived = function (s, e) {
        var offsetMessage = ", offset from session start: " + e.offset / 10000 + "ms.";
        if (e.offset === 0) {
            offsetMessage = "";
        }
        console.log("[" + (new Date()).toISOString() + "] Event received: " + e.description + offsetMessage);
    };

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

&lt;/div&gt;



&lt;p&gt;This function is responsible for creating and configuring an Avatar Synthesizer using the Speech SDK. Let’s break it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It creates a &lt;code&gt;SpeechConfig&lt;/code&gt;object from Azure subscription key (&lt;code&gt;cogSvcSubKey&lt;/code&gt;) and region (&lt;code&gt;cogSvcRegion&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Specifies the voice name for speech synthesis.&lt;/li&gt;
&lt;li&gt;Configures the video format for the avatar, including cropping settings.&lt;/li&gt;
&lt;li&gt;Defines an &lt;code&gt;AvatarConfig&lt;/code&gt;with character, style, and video format settings.&lt;/li&gt;
&lt;li&gt;Sets the background color for the avatar.&lt;/li&gt;
&lt;li&gt;Instantiates an &lt;code&gt;AvatarSynthesizer&lt;/code&gt;object using the configured Speech Config and Avatar Config.&lt;/li&gt;
&lt;li&gt;The function sets up an event handler for avatar events, logging relevant information.&lt;/li&gt;
&lt;li&gt;Returns the configured &lt;code&gt;avatarSynthesizer&lt;/code&gt;object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two functions play a crucial role in setting up the WebRTC connection and configuring the Avatar Synthesizer, providing a foundation for the avatar application.&lt;/p&gt;

&lt;p&gt;You must be wondering what is the config.js and what does it contain ?. Well don’t worry I will show my sample config.js&lt;/p&gt;

&lt;p&gt;Here is the sample config.js which you will have to create and put your keys in here…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const avatarAppConfig = {
    cogSvcRegion : "westus2",
    cogSvcSubKey : "YOUR SPEECH KEY",
    voiceName : "en-US-JennyNeural",
    avatarCharacter : "lisa",
    avatarStyle : "casual-sitting",
    avatarBackgroundColor : "#FFFFFFFF",
    iceUrl : "stun:relay.communication.microsoft.com:3478",
    iceUsername : "YOUR USERNAME",
    iceCredential : "YOUR CREDENTIAL"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3. React Component for Avatar Display and Interaction&lt;/strong&gt;&lt;br&gt;
Now we are ready with the utilities and configs, Lets create a simple UI.&lt;/p&gt;

&lt;p&gt;Let me first show you how the UI is going to look like&lt;/p&gt;

&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%2Fmwbbue93gssjvd9n1cri.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%2Fmwbbue93gssjvd9n1cri.png" alt="Image description" width="610" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a file Avatar.jsx and put the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "./Avatar.css";
import * as SpeechSDK from "microsoft-cognitiveservices-speech-sdk";
import { createAvatarSynthesizer, createWebRTCConnection } from "./Utility";
import { avatarAppConfig } from "./config";
import { useState } from "react";
import { useRef } from "react";

export const Avatar = () =&amp;gt; {

    const [avatarSynthesizer, setAvatarSynthesizer] = useState(null);
    const myAvatarVideoRef = useRef();
    const myAvatarVideoEleRef = useRef();
    const myAvatarAudioEleRef = useRef();
    const [mySpeechText, setMySpeechText] = useState("");

    var iceUrl = avatarAppConfig.iceUrl
    var iceUsername = avatarAppConfig.iceUsername
    var iceCredential = avatarAppConfig.iceCredential

    const handleSpeechText = (event) =&amp;gt; {
        setMySpeechText(event.target.value);
    }


    const handleOnTrack = (event) =&amp;gt; {

        console.log("#### Printing handle onTrack ",event);

        // Update UI elements
        console.log("Printing event.track.kind ",event.track.kind);
        if (event.track.kind === 'video') {
            const mediaPlayer = myAvatarVideoEleRef.current;
            mediaPlayer.id = event.track.kind;
            mediaPlayer.srcObject = event.streams[0];
            mediaPlayer.autoplay = true;
            mediaPlayer.playsInline = true;
            mediaPlayer.addEventListener('play', () =&amp;gt; {
            window.requestAnimationFrame(()=&amp;gt;{});
          });
        } else {
          // Mute the audio player to make sure it can auto play, will unmute it when speaking
          // Refer to https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
          //const mediaPlayer = myAvatarVideoEleRef.current;
          const audioPlayer = myAvatarAudioEleRef.current;
          audioPlayer.srcObject = event.streams[0];
          audioPlayer.autoplay = true;
          audioPlayer.playsInline = true;
          audioPlayer.muted = true;
        }
      };

    const stopSpeaking = () =&amp;gt; {
        avatarSynthesizer.stopSpeakingAsync().then(() =&amp;gt; {
          console.log("[" + (new Date()).toISOString() + "] Stop speaking request sent.")

        }).catch();
    }  

    const stopSession = () =&amp;gt; {

        try{
          //Stop speaking
          avatarSynthesizer.stopSpeakingAsync().then(() =&amp;gt; {
            console.log("[" + (new Date()).toISOString() + "] Stop speaking request sent.")
            // Close the synthesizer
            avatarSynthesizer.close();
          }).catch();
        }catch(e) {
        }
      }

    const speakSelectedText = () =&amp;gt; {

        //Start speaking the text
        const audioPlayer = myAvatarAudioEleRef.current;
        console.log("Audio muted status ",audioPlayer.muted);
        audioPlayer.muted = false;        
        avatarSynthesizer.speakTextAsync(mySpeechText).then(
            (result) =&amp;gt; {
                if (result.reason === SpeechSDK.ResultReason.SynthesizingAudioCompleted) {
                    console.log("Speech and avatar synthesized to video stream.")
                } else {
                    console.log("Unable to speak. Result ID: " + result.resultId)
                    if (result.reason === SpeechSDK.ResultReason.Canceled) {
                        let cancellationDetails = SpeechSDK.CancellationDetails.fromResult(result)
                        console.log(cancellationDetails.reason)
                        if (cancellationDetails.reason === SpeechSDK.CancellationReason.Error) {
                            console.log(cancellationDetails.errorDetails)
                        }
                    }
                }
        }).catch((error) =&amp;gt; {
            console.log(error)
            avatarSynthesizer.close()
        });
    }

    const startSession = () =&amp;gt; {

        let peerConnection = createWebRTCConnection(iceUrl,iceUsername, iceCredential);
        console.log("Peer connection ",peerConnection);
        peerConnection.ontrack = handleOnTrack;
        peerConnection.addTransceiver('video', { direction: 'sendrecv' })
        peerConnection.addTransceiver('audio', { direction: 'sendrecv' })

        let avatarSynthesizer = createAvatarSynthesizer();
        setAvatarSynthesizer(avatarSynthesizer);
        peerConnection.oniceconnectionstatechange = e =&amp;gt; {
            console.log("WebRTC status: " + peerConnection.iceConnectionState)

            if (peerConnection.iceConnectionState === 'connected') {
                console.log("Connected to Azure Avatar service");
            }

            if (peerConnection.iceConnectionState === 'disconnected' || peerConnection.iceConnectionState === 'failed') {
                console.log("Azure Avatar service Disconnected");
            }
        }

        avatarSynthesizer.startAvatarAsync(peerConnection).then((r) =&amp;gt; {
            console.log("[" + (new Date()).toISOString() + "] Avatar started.")

        }).catch(
            (error) =&amp;gt; {
                console.log("[" + (new Date()).toISOString() + "] Avatar failed to start. Error: " + error)
            }
        );
    }



    return(
        &amp;lt;div className="container myAvatarContainer"&amp;gt;
            &amp;lt;p className="myAvatarDemoText"&amp;gt;Azure Avatar Demo&amp;lt;/p&amp;gt;
            &amp;lt;div className="container myAvatarVideoRootDiv d-flex justify-content-around"&amp;gt;
                &amp;lt;div  className="myAvatarVideo"&amp;gt;
                    &amp;lt;div id="myAvatarVideo" className="myVideoDiv" ref={myAvatarVideoRef}&amp;gt;

                        &amp;lt;video className="myAvatarVideoElement" ref={myAvatarVideoEleRef}&amp;gt;

                        &amp;lt;/video&amp;gt;

                        &amp;lt;audio ref={myAvatarAudioEleRef}&amp;gt;

                        &amp;lt;/audio&amp;gt;
                    &amp;lt;/div&amp;gt;
                    &amp;lt;div className="myButtonGroup d-flex justify-content-around"&amp;gt;
                        &amp;lt;button className="btn btn-success"
                            onClick={startSession}&amp;gt;
                            Connect
                        &amp;lt;/button&amp;gt;
                        &amp;lt;button className="btn btn-danger"
                            onClick={stopSession}&amp;gt;
                            Disconnect
                        &amp;lt;/button&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div className="myTextArea"&amp;gt;

                    &amp;lt;textarea className="myTextArea" onChange={handleSpeechText}&amp;gt;

                    &amp;lt;/textarea&amp;gt;
                    &amp;lt;div className="myButtonGroup d-flex justify-content-around"&amp;gt;
                        &amp;lt;button className="btn btn-success" onClick={speakSelectedText}&amp;gt;
                            Speak
                        &amp;lt;/button&amp;gt;
                        &amp;lt;button className="btn btn-warning" onClick={stopSpeaking}&amp;gt;
                            Stop
                        &amp;lt;/button&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the css code for Avatar.jsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.myAvatarDemoText {
    font-size: larger;
    font-family: "Poppins";
    font-weight: 600;
}

.myAvatarContainer {
    text-align: center;
    margin-top: 5rem;
}

.myAvatarVideoRootDiv {
    margin-top: 3rem;
}

.myTextArea {
    height: 11rem;
    width: 35rem;
    border-radius: 5px;
    border-color: grey;
}

.myAvatarVideo {
    /* background-color: grey; */
    height: 20rem;
    width: 13rem;
    border-radius: 8px;
}

.myVideoDiv {
    height: 22rem;
    margin-bottom: 2rem;
}

video {
    margin: 0px 0px 20px 0px;
    padding-right: 5rem;
    width: 20rem;
    height: 22rem;
    border-radius: 8px;
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the component in App.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Avatar } from './components/Avatar';

function App() {
  return (
    &amp;lt;div className="App"&amp;gt;
        &amp;lt;Avatar/&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Let's start project&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You can check the application at &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lets test the Avatar AI 😄&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Initially you will not see any avatar, You need to click on Connect button to get the Avatar loaded.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After Avatar Connect, Paste the text into the box and press on speak and let the magic begin 😃&lt;/p&gt;

&lt;p&gt;You will see that avatar actually speaks the text ! . Isn’t it amazing?&lt;/p&gt;

&lt;p&gt;Congratulations! You’ve successfully set up a Text-to-Speech Avatar application using ReactJS and Azure AI. This powerful feature allows you to integrate lifelike synthetic talking avatars into your applications seamlessly. Feel free to customize the application further based on your requirements.&lt;/p&gt;

&lt;p&gt;Explore more about Azure AI Text-to-Speech Avatar &lt;a href="https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech-avatar/what-is-text-to-speech-avatar" rel="noopener noreferrer"&gt;here&lt;/a&gt; and experiment with different settings and configurations to enhance your avatar’s capabilities. Happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thanks for reading&lt;/strong&gt;&lt;br&gt;
Want to work together? Contact me on &lt;a href="https://t.me/mhcrocky" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am an freelancer have much much experience in React.js. Want to catch the journey? Follow me on &lt;a href="https://github.com/mhcrocky" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>texttospeech</category>
      <category>azure</category>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Laravel 10.x is released</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 15 Feb 2023 18:38:03 +0000</pubDate>
      <link>https://dev.to/mhcrocky/laravel-10x-is-released-56ac</link>
      <guid>https://dev.to/mhcrocky/laravel-10x-is-released-56ac</guid>
      <description></description>
      <category>help</category>
      <category>debugging</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to add CSS class in Ruby on rails form elements</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:15:23 +0000</pubDate>
      <link>https://dev.to/mhcrocky/how-to-add-css-class-in-ruby-on-rails-form-elements-14hm</link>
      <guid>https://dev.to/mhcrocky/how-to-add-css-class-in-ruby-on-rails-form-elements-14hm</guid>
      <description>&lt;p&gt;This post is just a mere attempt to log-in what I have learnt about Ruby on rails Form and is not a tutorial but I know this will help those who have just began to learn Rails.&lt;/p&gt;

&lt;p&gt;I started to building project using bootstrap and ruby on rails and meet some problem.&lt;/p&gt;

&lt;p&gt;I wanted to use &lt;em&gt;&lt;strong&gt;form-control&lt;/strong&gt;&lt;/em&gt; class in the &lt;em&gt;&lt;strong&gt;input&lt;/strong&gt;&lt;/em&gt; tag to style it, but it didn't work.&lt;br&gt;
So I did a Google search and found the results, but it took me a while.&lt;/p&gt;

&lt;p&gt;The reason I want to write this article is because I don't want other people like me to waste their time.&lt;/p&gt;

&lt;p&gt;You can add class using following codes&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;% form.password_field(:account, class: "form-control") %&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;You can add other attributes to this field.&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;% form.password_field(:account, class: "form-control" , data:"dsfsdfsdf") %&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;If this article help you I will be happy &lt;/p&gt;

</description>
      <category>ruby</category>
      <category>webdev</category>
      <category>rails</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Dark mode in Next.js using Tailwind CSS</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:15:07 +0000</pubDate>
      <link>https://dev.to/mhcrocky/dark-mode-in-nextjs-using-tailwind-css-2n83</link>
      <guid>https://dev.to/mhcrocky/dark-mode-in-nextjs-using-tailwind-css-2n83</guid>
      <description>&lt;p&gt;Now that dark mode is a first-class feature of many operating systems, it’s becoming more and more common to design a dark version of your website to go along with the default design.&lt;/p&gt;

&lt;p&gt;Tailwind includes a dark variant that lets you style your site differently when dark mode is enabled:&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;div class="bg-white dark:bg-slate-900" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only problem now is to implement a theme manager. But there is no need to reinvent the wheel here, there is an excellent package called next-themes.&lt;/p&gt;

&lt;p&gt;To setup dark mode you need enable dark mode in &lt;code&gt;tailwind.config.js&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;darkMode: "class"

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

&lt;/div&gt;



&lt;p&gt;Then enable class mode in next-themes &lt;code&gt;_app.tsx&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;import { ThemeProvider } from "next-themes";
// ...
function MyApp({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;ThemeProvider
        attribute="class"
        defaultTheme="system"
        storageKey="some-key-for-your-application"
      &amp;gt;
         &amp;lt;Component {...pageProps} /&amp;gt;
      &amp;lt;/ThemeProvider&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can create a reusable theme changer component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useTheme } from 'next-themes'

const ThemeChanger = () =&amp;gt; {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

  // When mounted on client, now we can show the UI
  useEffect(() =&amp;gt; setMounted(true), [])

  if (!mounted) return null

  return (
    &amp;lt;div&amp;gt;
      The current theme is: {theme}
      &amp;lt;button onClick={() =&amp;gt; setTheme('light')}&amp;gt;Light Mode&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setTheme('dark')}&amp;gt;Dark Mode&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now any classes with dark variant will active when you toggle themes.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Extending with variables&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
You can extend this feature with CSS variables. This will allow you to add single class in classNames but allow for both themes and it much better for customizing your website to your brand liking.&lt;/p&gt;

&lt;p&gt;Setup CSS variables in &lt;code&gt;globals.css&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;.light {
  --color-text-base: #3c4043;
  --color-text-muted: #5f6368;
  --color-background-base: #ffffff;
}
.dark {
  --color-text-base: #e8eaed;
  --color-text-muted: #9aa0a6;
  --color-background-base: #202124;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup up classes in &lt;code&gt;tailwind.config.js&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;// ...
theme: {
    extend: {
      textColor: {
        skin: {
          base: "var(--color-text-base)",
          muted: "var(--color-text-muted)",
        },
      },
      backgroundColor: {
        skin: {
          base: "var(--color-background-base)",
        },
      },
    },
  },
// ...

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

&lt;/div&gt;



&lt;p&gt;Example code: &lt;code&gt;globals.scss&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;@layer base {
  html {
    @apply h-full;
  }
  body {
    @apply text-skin-base bg-skin-base h-full;
  }

  div#__next {
    @apply h-full;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Fixing Tailwind VS Code linting errors</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:14:49 +0000</pubDate>
      <link>https://dev.to/mhcrocky/fixing-tailwind-vs-code-linting-errors-4a7g</link>
      <guid>https://dev.to/mhcrocky/fixing-tailwind-vs-code-linting-errors-4a7g</guid>
      <description>&lt;p&gt;When you are using Tailwind with VS Code you might run across a linting error in extracted styles.&lt;/p&gt;

&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%2Fb2f8c5q1zd8tg1vlcuz7.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%2Fb2f8c5q1zd8tg1vlcuz7.png" alt="Image description" width="672" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fear not here is how to fix it!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a workspace settings folder aka .vscode folder.
Create it at the root of your project. Then add a settings.json file inside the folder with these options.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "editor.formatOnSave": true,
  // Run Stylelint fix when you save the file
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": true
  },
  // Recommended config for the extension
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now we need a way to validate CSS since we disabled the native rules
We are going to use a popular extension called Stylelint.
Install the extension and let's add some recommended rules and plugins
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add stylelint stylelint-config-recommended-scss stylelint-order stylelint-scss --dev

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Then create a &lt;code&gt;.stylelintrc.json&lt;/code&gt; file at the root.
Use these configurations
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "extends": "stylelint-config-recommended-scss",
  "plugins": ["stylelint-order", "stylelint-scss"],
  "rules": {
    "order/properties-alphabetical-order": true,
    "scss/at-rule-no-unknown": null,
    "scss/at-import-no-partial-leading-underscore": null,
    "selector-pseudo-class-no-unknown": null,
    "at-rule-no-unknown": [
      true,
      {
        "ignoreAtRules": [
          "tailwind",
          "apply",
          "variants",
          "responsive",
          "screen"
        ]
      }
    ],
    "declaration-block-trailing-semicolon": null,
    "no-descending-specificity": null
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Creating a reusable Button component with React and Tailwind CSS</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:14:25 +0000</pubDate>
      <link>https://dev.to/mhcrocky/creating-a-reusable-button-component-with-react-and-tailwind-css-4dh5</link>
      <guid>https://dev.to/mhcrocky/creating-a-reusable-button-component-with-react-and-tailwind-css-4dh5</guid>
      <description>&lt;p&gt;Tailwind CSS is a CSS framework that gives you utility classes instead of pre-built components. In this article, we will write a reusable Button component that can have different variants and sizes while also being customizable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a project
&lt;/h2&gt;

&lt;p&gt;First, you need to install tailwind. &lt;/p&gt;

&lt;h3&gt;
  
  
  Install tailwind
&lt;/h3&gt;

&lt;p&gt;Install the dependecies as dev dependecies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# npm
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
# yarn
yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create your configuration files
&lt;/h3&gt;

&lt;p&gt;Next, generate your &lt;code&gt;tailwind.config.js&lt;/code&gt; and &lt;code&gt;postcss.config.js&lt;/code&gt; files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# npm
npx tailwindcss init -p
# yarn
yarn tailwindcss init -p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include Tailwind in your CSS&lt;br&gt;
You need to add these 3 lines to your global/index CSS file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@tailwind base;
@tailwind components;
@tailwind utilities;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Protip: If you are using VS Code you should install the Tailwind CSS IntelliSense&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can check if tailwind is working correctly by rendering this button element.&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;button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"&amp;gt;
  Enable
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If not working properly please refer to the documentation page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a button component
&lt;/h3&gt;

&lt;p&gt;Let's create reusable button from the snippet earlier. I am using typescript for better prop validation.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Button.tsx
import { forwardRef } from "react";

interface ButtonOptions {}

type Ref = HTMLButtonElement;

expor_t type ButtonProps = React.DetailedHTMLProps&amp;lt;
  React.ButtonHTMLAttributes&amp;lt;HTMLButtonElement&amp;gt;,
  HTMLButtonElement
&amp;gt; &amp;amp;
  ButtonOptions;

const Button = forwardRef&amp;lt;Ref, ButtonProps&amp;gt;((props, ref) =&amp;gt; {
  const { type = "button", children, ...rest } = props;
  return (
    &amp;lt;button
      ref={ref}
      className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
      {...rest}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
});

Button.displayName = "Button";
export default Button;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome now we can use our button like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// import Button from "../src/components/Button/Button";
&amp;lt;Button&amp;gt;Anything&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if I wanted a red button on the home page. We can pass a &lt;code&gt;bg-red-500&lt;/code&gt; class to our button component but that would overwrite the className prop removing our button styles. So we need to merge our className props.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merging classes
&lt;/h3&gt;

&lt;p&gt;We can use a module called &lt;code&gt;clsx&lt;/code&gt;to merge our classes.&lt;br&gt;
Our button component looks like this now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Button = forwardRef&amp;lt;Ref, ButtonProps&amp;gt;((props, ref) =&amp;gt; {
  const { type = "button", className, children, ...rest } = props;

  const merged = clsx(
    "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded",
    className
  );

  return (
    &amp;lt;button ref={ref} className={merged} {...rest}&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  But our button's background color is not changing what is happening?
&lt;/h4&gt;

&lt;p&gt;Well, it's due to CSS Precedence if you inspect the element in dev tools you can see the bg-red-500 class is applying. But also is bg-blue-500.&lt;/p&gt;

&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%2Fp280y9f909xugml2icbc.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%2Fp280y9f909xugml2icbc.png" alt="Image description" width="672" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix this you need to extract the classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extracting styles
&lt;/h3&gt;

&lt;p&gt;We can use tailwind’s &lt;a class="mentioned-user" href="https://dev.to/apply"&gt;@apply&lt;/a&gt; directive to easily extract common utility patterns to CSS component classes.&lt;br&gt;
We are going the use &lt;code&gt;sass&lt;/code&gt;so install the package &lt;code&gt;sass&lt;/code&gt;.&lt;br&gt;
Then create a &lt;code&gt;scss&lt;/code&gt;file in the same directory as the component file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// button.scss
.btn {
  @apply bg-blue-500 hover:bg-blue-700 text-white font-bold;
  @apply py-2 px-4;
  @apply rounded;
}
Update our button's merged style:

// Button.tsx
const merged = clsx("btn", className);
After that we need to import this file to the global/index scss file.

@tailwind base;
@tailwind components;

// add your component styles from here
@import "../src/components/Button/button.scss";

// to here
@tailwind utilities;


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

&lt;/div&gt;



&lt;p&gt;Now we can override any default styles that our button component have&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;Button className="bg-red-500 hover:bg-red-400 px-12 py-4 text-lg"&amp;gt;
    Disable
  &amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yay 🙌 you got a reusable button component. Let's spice it up by adding variants&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding variants
&lt;/h2&gt;

&lt;p&gt;Let's add an outline and ghost variant to our button component. Extend the &lt;code&gt;button.scss&lt;/code&gt; file to 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;.btn {
  @apply bg-blue-500 hover:bg-blue-700 text-white font-bold;
  @apply py-2 px-4;
  @apply rounded;
  @apply transition-colors;
}

.btn-outline {
  @apply border-2 border-blue-300 bg-opacity-0 text-blue-500;
  &amp;amp;:hover,
  &amp;amp;:focus {
    @apply bg-opacity-10;
  }
}

.btn-ghost {
  @apply border-2 border-transparent bg-opacity-0 text-blue-500;
  &amp;amp;:hover,
  &amp;amp;:focus {
    @apply border-blue-300;
  }
}

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

&lt;/div&gt;



&lt;p&gt;Let's change our button component to take a &lt;code&gt;variant&lt;/code&gt;prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import clsx from "clsx";
import { forwardRef } from "react";

interface ButtonOptions {
  /**
   * Button display variants
   * @default "solid"
   * @type ButtonVariant
   */
  variant?: ButtonVariant;
}

type Ref = HTMLButtonElement;

export type ButtonProps = React.DetailedHTMLProps&amp;lt;
  React.ButtonHTMLAttributes&amp;lt;HTMLButtonElement&amp;gt;,
  HTMLButtonElement
&amp;gt; &amp;amp;
  ButtonOptions;

type ButtonVariant = "outline" | "solid" | "ghost";

const getVariant = (variant: ButtonVariant) =&amp;gt; {
  switch (variant) {
    case "outline":
      return "btn-outline";
    case "ghost":
      return "btn-ghost";
    default:
      return undefined;
  }
};

const Button = forwardRef&amp;lt;Ref, ButtonProps&amp;gt;((props, ref) =&amp;gt; {
  const {
    variant = "solid",
    type = "button",
    className,
    children,
    ...rest
  } = props;

  const merged = clsx("btn", getVariant(variant), className);

  return (
    &amp;lt;button ref={ref} className={merged} {...rest}&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
});

Button.displayName = "Button";
export default Button;

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

&lt;/div&gt;



&lt;p&gt;Now you can use it like this:&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;Button variant="ghost"&amp;gt;Ghost&amp;lt;/Button&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;🎉 Woohoo, you just made a reusable button with custom props!&lt;/p&gt;

&lt;p&gt;You can extend this to add more variants, sizing, and other props but in general, this is how we build reusable components with React and Tailwind CSS.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Next.js 12 Authentication Middleware with Supabase</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:13:03 +0000</pubDate>
      <link>https://dev.to/mhcrocky/nextjs-12-authentication-middleware-with-supabase-h03</link>
      <guid>https://dev.to/mhcrocky/nextjs-12-authentication-middleware-with-supabase-h03</guid>
      <description>&lt;p&gt;Nextjs 12 is released with a new feature called Middlewares. It allows us to run code before a request is completed. We can use this feature for protected routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up
&lt;/h3&gt;

&lt;p&gt;You can follow this repo for a simple setup.&lt;br&gt;
Then create a new folder that groups all protected routes and create a _middleware.ts file. For example, I have named the folder user since it's all user personalized routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pages
│   _app.tsx
|  index.tsx
│   ....
└───user
│   │  _middleware.ts
│   │   dashboard.tsx
│   │   settings.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import jwt from "@tsndr/cloudflare-worker-jwt";
import { NextRequest, NextResponse } from "next/server";

/**
 * Verifies the user's JWT token and returns the payload if
 * it's valid or a response if it's not.
 */
export async function middleware(request: NextRequest) {
  const token = request.cookies["sb:token"]; //'sb:token' is the default cookie name

  if (!token || !(await jwt.verify(token, process.env.SUPABASE_JWT_SECRET!))) {
    return NextResponse.redirect("/login", 302); // If a user is not authenticated (either no token was send, or the token is invalid) redirect the user to the homepage where they will be presented with a log-in screen
  }

  return NextResponse.next(); // continue the middleware chain https://nextjs.org/docs/api-reference/next/server#nextresponse
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code checks if the cookies set by supabase is valid.&lt;br&gt;
And now &lt;code&gt;dashboard&lt;/code&gt;, &lt;code&gt;settings&lt;/code&gt;pages require authentication now without adding any code in them, or wrapping in HOC. 🎉&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SeverLess programming with React + Supabase - 1</title>
      <dc:creator>mhcrocky</dc:creator>
      <pubDate>Wed, 09 Nov 2022 08:10:01 +0000</pubDate>
      <link>https://dev.to/mhcrocky/severless-programming-with-react-supabase-1-5m8</link>
      <guid>https://dev.to/mhcrocky/severless-programming-with-react-supabase-1-5m8</guid>
      <description>&lt;p&gt;This example provides the steps to build a basic user management app. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supabase Database: a Postgres database for storing your user data.&lt;/li&gt;
&lt;li&gt;Supabase Auth: users can sign in with magic links (no passwords, only email).&lt;/li&gt;
&lt;li&gt;Supabase Storage: users can upload a photo.&lt;/li&gt;
&lt;li&gt;Row Level Security: data is protected so that individuals can only access their own data.&lt;/li&gt;
&lt;li&gt;Instant APIs: APIs will be automatically generated when you create your database tables.
By the end of this guide you'll have an app which allows users to login and update some basic profile details:&lt;/li&gt;
&lt;/ul&gt;

&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%2F8gxh4iusuffc4g6fnrfz.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%2F8gxh4iusuffc4g6fnrfz.png" alt="Image description" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project set up
&lt;/h2&gt;

&lt;p&gt;Before we start building we're going to set up our Database and API. This is as simple as starting a new Project in Supabase and then creating a "schema" inside the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a project
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to app.supabase.com.&lt;/li&gt;
&lt;li&gt;Click on "New Project".&lt;/li&gt;
&lt;li&gt;Enter your project details.&lt;/li&gt;
&lt;li&gt;Wait for the new database to launch.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Set up the database schema
&lt;/h3&gt;

&lt;p&gt;Now we are going to set up the database schema. We can use the "User Management Starter" quickstart in the SQL Editor, or you can just copy/paste the SQL from below and run it yourself.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the SQL Editor page in the Dashboard.&lt;/li&gt;
&lt;li&gt;Click User Management Starter.&lt;/li&gt;
&lt;li&gt;Click Run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;SQL&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;-- Create a table for public profiles
create table profiles (
  id uuid references auth.users not null primary key,
  updated_at timestamp with time zone,
  username text unique,
  full_name text,
  avatar_url text,
  website text,

  constraint username_length check (char_length(username) &amp;gt;= 3)
);
-- Set up Row Level Security (RLS)
-- See https://supabase.com/docs/guides/auth/row-level-security for more details.
alter table profiles
  enable row level security;

create policy "Public profiles are viewable by everyone." on profiles
  for select using (true);

create policy "Users can insert their own profile." on profiles
  for insert with check (auth.uid() = id);

create policy "Users can update own profile." on profiles
  for update using (auth.uid() = id);

-- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth.
-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details.
create function public.handle_new_user()
returns trigger as $$
begin
  insert into public.profiles (id, full_name, avatar_url)
  values (new.id, new.raw_user_meta_data-&amp;gt;&amp;gt;'full_name', new.raw_user_meta_data-&amp;gt;&amp;gt;'avatar_url');
  return new;
end;
$$ language plpgsql security definer;
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();

-- Set up Storage!
insert into storage.buckets (id, name)
  values ('avatars', 'avatars');

-- Set up access controls for storage.
-- See https://supabase.com/docs/guides/storage#policy-examples for more details.
create policy "Avatar images are publicly accessible." on storage.objects
  for select using (bucket_id = 'avatars');

create policy "Anyone can upload an avatar." on storage.objects
  for insert with check (bucket_id = 'avatars');

create policy "Anyone can update their own avatar." on storage.objects
  for update using (auth.uid() = owner) with check (bucket_id = 'avatars');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Get the API Keys
&lt;/h3&gt;

&lt;p&gt;Now that you've created some database tables, you are ready to insert data using the auto-generated API. We just need to get the URL and anon key from the API settings.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the Settings page in the Dashboard.&lt;/li&gt;
&lt;li&gt;Click API in the sidebar.&lt;/li&gt;
&lt;li&gt;Find your API URL, anon, and service_role keys on this page.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building the App
&lt;/h2&gt;

&lt;p&gt;Let's start building the React app from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize a React app
&lt;/h3&gt;

&lt;p&gt;We can use Create React App to initialize an app called supabase-react:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app supabase-react
cd supabase-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then let's install the only additional dependency: supabase-js.&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 @supabase/supabase-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally we want to save the environment variables in a .env. All we need are the API URL and the anon key that you copied earlier.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.env&lt;br&gt;
&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;REACT_APP_SUPABASE_URL=YOUR_SUPABASE_URL
REACT_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed on the browser, and that's completely fine since we have Row Level Security enabled on our Database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/supabaseClient.js&lt;br&gt;
&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;import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseAnonKey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And one optional step is to update the CSS file src/index.css to make the app look nice. You can find the full contents of this file here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up a Login component
&lt;/h3&gt;

&lt;p&gt;Let's set up a React component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/Auth.js&lt;br&gt;
&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;import { useState } from 'react'
import { supabase } from './supabaseClient'

export default function Auth() {
  const [loading, setLoading] = useState(false)
  const [email, setEmail] = useState('')

  const handleLogin = async (e) =&amp;gt; {
    e.preventDefault()

    try {
      setLoading(true)
      const { error } = await supabase.auth.signInWithOtp({ email })
      if (error) throw error
      alert('Check your email for the login link!')
    } catch (error) {
      alert(error.error_description || error.message)
    } finally {
      setLoading(false)
    }
  }

  return (
    &amp;lt;div className="row flex-center flex"&amp;gt;
      &amp;lt;div className="col-6 form-widget" aria-live="polite"&amp;gt;
        &amp;lt;h1 className="header"&amp;gt;Supabase + React&amp;lt;/h1&amp;gt;
        &amp;lt;p className="description"&amp;gt;Sign in via magic link with your email below&amp;lt;/p&amp;gt;
        {loading ? (
          'Sending magic link...'
        ) : (
          &amp;lt;form onSubmit={handleLogin}&amp;gt;
            &amp;lt;label htmlFor="email"&amp;gt;Email&amp;lt;/label&amp;gt;
            &amp;lt;input
              id="email"
              className="inputField"
              type="email"
              placeholder="Your email"
              value={email}
              onChange={(e) =&amp;gt; setEmail(e.target.value)}
            /&amp;gt;
            &amp;lt;button className="button block" aria-live="polite"&amp;gt;
              Send magic link
            &amp;lt;/button&amp;gt;
          &amp;lt;/form&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Account page
&lt;/h3&gt;

&lt;p&gt;After a user is signed in we can allow them to edit their profile details and manage their account.&lt;/p&gt;

&lt;p&gt;Let's create a new component for that called Account.js.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/Account.js&lt;br&gt;
&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;import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'

const Account = ({ session }) =&amp;gt; {
  const [loading, setLoading] = useState(true)
  const [username, setUsername] = useState(null)
  const [website, setWebsite] = useState(null)
  const [avatar_url, setAvatarUrl] = useState(null)

  useEffect(() =&amp;gt; {
    getProfile()
  }, [session])

  const getProfile = async () =&amp;gt; {
    try {
      setLoading(true)
      const { user } = session

      let { data, error, status } = await supabase
        .from('profiles')
        .select(`username, website, avatar_url`)
        .eq('id', user.id)
        .single()

      if (error &amp;amp;&amp;amp; status !== 406) {
        throw error
      }

      if (data) {
        setUsername(data.username)
        setWebsite(data.website)
        setAvatarUrl(data.avatar_url)
      }
    } catch (error) {
      alert(error.message)
    } finally {
      setLoading(false)
    }
  }

  const updateProfile = async (e) =&amp;gt; {
    e.preventDefault()

    try {
      setLoading(true)
      const { user } = session

      const updates = {
        id: user.id,
        username,
        website,
        avatar_url,
        updated_at: new Date(),
      }

      let { error } = await supabase.from('profiles').upsert(updates)

      if (error) {
        throw error
      }
    } catch (error) {
      alert(error.message)
    } finally {
      setLoading(false)
    }
  }

  return (
    &amp;lt;div aria-live="polite"&amp;gt;
      {loading ? (
        'Saving ...'
      ) : (
        &amp;lt;form onSubmit={updateProfile} className="form-widget"&amp;gt;
          &amp;lt;div&amp;gt;Email: {session.user.email}&amp;lt;/div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;label htmlFor="username"&amp;gt;Name&amp;lt;/label&amp;gt;
            &amp;lt;input
              id="username"
              type="text"
              value={username || ''}
              onChange={(e) =&amp;gt; setUsername(e.target.value)}
            /&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;label htmlFor="website"&amp;gt;Website&amp;lt;/label&amp;gt;
            &amp;lt;input
              id="website"
              type="url"
              value={website || ''}
              onChange={(e) =&amp;gt; setWebsite(e.target.value)}
            /&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div&amp;gt;
            &amp;lt;button className="button primary block" disabled={loading}&amp;gt;
              Update profile
            &amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/form&amp;gt;
      )}
      &amp;lt;button type="button" className="button block" onClick={() =&amp;gt; supabase.auth.signOut()}&amp;gt;
        Sign Out
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default Account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Launch!
&lt;/h3&gt;

&lt;p&gt;Now that we have all the components in place, let's update App.js:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/App.js&lt;br&gt;
&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;import './index.css'
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
import Auth from './Auth'
import Account from './Account'

export default function App() {
  const [session, setSession] = useState(null)

  useEffect(() =&amp;gt; {
    supabase.auth.getSession().then(({ data: { session } }) =&amp;gt; {
      setSession(session)
    })

    supabase.auth.onAuthStateChange((_event, session) =&amp;gt; {
      setSession(session)
    })
  }, [])

  return (
    &amp;lt;div className="container" style={{ padding: '50px 0 100px 0' }}&amp;gt;
      {!session ? &amp;lt;Auth /&amp;gt; : &amp;lt;Account key={session.user.id} session={session} /&amp;gt;}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that's done, run this in a terminal window:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And then open the browser to localhost:3000 and you should see the completed app.&lt;/p&gt;

&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%2Fyf905q17db8cr92rai8f.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%2Fyf905q17db8cr92rai8f.png" alt="Image description" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Profile photos
&lt;/h2&gt;

&lt;p&gt;Every Supabase project is configured with Storage for managing large files like photos and videos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an upload widget
&lt;/h3&gt;

&lt;p&gt;Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:&lt;br&gt;
&lt;code&gt;src/Avatar.js&lt;br&gt;
&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;import { useEffect, useState } from 'react'
import { supabase } from './supabaseClient'

export default function Avatar({ url, size, onUpload }) {
  const [avatarUrl, setAvatarUrl] = useState(null)
  const [uploading, setUploading] = useState(false)

  useEffect(() =&amp;gt; {
    if (url) downloadImage(url)
  }, [url])

  const downloadImage = async (path) =&amp;gt; {
    try {
      const { data, error } = await supabase.storage.from('avatars').download(path)
      if (error) {
        throw error
      }
      const url = URL.createObjectURL(data)
      setAvatarUrl(url)
    } catch (error) {
      console.log('Error downloading image: ', error.message)
    }
  }

  const uploadAvatar = async (event) =&amp;gt; {
    try {
      setUploading(true)

      if (!event.target.files || event.target.files.length === 0) {
        throw new Error('You must select an image to upload.')
      }

      const file = event.target.files[0]
      const fileExt = file.name.split('.').pop()
      const fileName = `${Math.random()}.${fileExt}`
      const filePath = `${fileName}`

      let { error: uploadError } = await supabase.storage.from('avatars').upload(filePath, file)

      if (uploadError) {
        throw uploadError
      }

      onUpload(filePath)
    } catch (error) {
      alert(error.message)
    } finally {
      setUploading(false)
    }
  }

  return (
    &amp;lt;div style={{ width: size }} aria-live="polite"&amp;gt;
      &amp;lt;img
        src={avatarUrl ? avatarUrl : `https://place-hold.it/${size}x${size}`}
        alt={avatarUrl ? 'Avatar' : 'No image'}
        className="avatar image"
        style={{ height: size, width: size }}
      /&amp;gt;
      {uploading ? (
        'Uploading...'
      ) : (
        &amp;lt;&amp;gt;
          &amp;lt;label className="button primary block" htmlFor="single"&amp;gt;
            Upload an avatar
          &amp;lt;/label&amp;gt;
          &amp;lt;div className="visually-hidden"&amp;gt;
            &amp;lt;input
              type="file"
              id="single"
              accept="image/*"
              onChange={uploadAvatar}
              disabled={uploading}
            /&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the new widget
&lt;/h3&gt;

&lt;p&gt;And then we can add the widget to the Account page:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/Account.js&lt;br&gt;
&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;// Import the new component
import Avatar from './Avatar'

// ...

return (
  &amp;lt;div className="form-widget"&amp;gt;
    {/* Add to the body */}
    &amp;lt;Avatar
      url={avatar_url}
      size={150}
      onUpload={(url) =&amp;gt; {
        setAvatarUrl(url)
        updateProfile({ username, website, avatar_url: url })
      }}
    /&amp;gt;
    {/* ... */}
  &amp;lt;/div&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>supabase</category>
      <category>react</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
