<?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: C.S. Rhymes</title>
    <description>The latest articles on DEV Community by C.S. Rhymes (@chrisrhymes).</description>
    <link>https://dev.to/chrisrhymes</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%2F126250%2Fcd0a60d8-b257-45ea-9dee-90377556a18f.jpg</url>
      <title>DEV Community: C.S. Rhymes</title>
      <link>https://dev.to/chrisrhymes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chrisrhymes"/>
    <language>en</language>
    <item>
      <title>Hosting a Next.js site with AWS Elastic Beanstalk</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/hosting-a-nextjs-site-with-aws-elastic-beanstalk-2dnb</link>
      <guid>https://dev.to/chrisrhymes/hosting-a-nextjs-site-with-aws-elastic-beanstalk-2dnb</guid>
      <description>&lt;p&gt;I thought I’d share some learnings about how to host a Next.js site with AWS Elastic Beanstalk. This is a minimum configuration to get a basic site up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;In this example, we are going to create a new Next.js project, but the process should apply to existing projects too.&lt;/p&gt;

&lt;p&gt;The following command creates a folder called ‘my-app’ and creates a new Next.js app&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-next-app@latest my-app --yes
cd my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to create a few files so that the Next.js app is built on the server before it is deployed, then we need to tell AWS Elastic Beanstalk how to start the Next.js project. There is also a little configuration that will help our site deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks
&lt;/h2&gt;

&lt;p&gt;When you set the platform to node.js, Elastic Beanstalk should automatically run &lt;code&gt;npm install&lt;/code&gt; to install your dependencies, but it doesn’t automatically run the &lt;code&gt;next build&lt;/code&gt; that is needed for a production site.&lt;/p&gt;

&lt;p&gt;There are a couple of ways that you can get around this. One way is to build your app locally and zip it up, then deploy that build, or you can add some hook scripts to run the build on the server. We are going to run the build on the server, as this allows us to set environment variables in Elastic Beanstalk and use them when the site is built.&lt;/p&gt;

&lt;p&gt;Beanstalk offers both deployment hooks and configuration hooks.&lt;/p&gt;

&lt;p&gt;Deployment hooks are run when you push up code changes and configuration hooks run when you update configuration or environment variables through the web console or CLI.&lt;/p&gt;

&lt;p&gt;When you update an environment variable, you need to re-run &lt;code&gt;next build&lt;/code&gt; to build the code with the latest environment variables.&lt;/p&gt;

&lt;p&gt;Make a .platform directory in your project root, then &lt;code&gt;hooks&lt;/code&gt; and &lt;code&gt;confighooks&lt;/code&gt; directories, then create a &lt;code&gt;predeploy&lt;/code&gt; directory within each.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.platform/ 

&lt;ul&gt;
&lt;li&gt;hooks/ &lt;/li&gt;
&lt;li&gt;predeploy/&lt;/li&gt;
&lt;li&gt;confighooks/ &lt;/li&gt;
&lt;li&gt;predeploy/&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;We can create a &lt;code&gt;01_build.sh&lt;/code&gt; script file in both the &lt;code&gt;.platform/hooks/predeploy&lt;/code&gt; and &lt;code&gt;.platform/confighooks/predeploy&lt;/code&gt; directories to run the build when either the code is pushed or the config is changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

set -e

cd /var/app/staging
npm install
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to use the &lt;code&gt;predeploy&lt;/code&gt; hook as this runs after the code has been checked out, but before the code is deployed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beanstalk creates a &lt;code&gt;/var/app/staging&lt;/code&gt; directory to do the build work,&lt;/li&gt;
&lt;li&gt;It then deletes the &lt;code&gt;/var/app/current&lt;/code&gt; directory,&lt;/li&gt;
&lt;li&gt;It then renames the &lt;code&gt;/var/app/staging&lt;/code&gt; directory to &lt;code&gt;/var/app/current&lt;/code&gt; for your live app&lt;/li&gt;
&lt;li&gt;Then it starts up the service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More information about &lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/platforms-linux-extend.hooks.html" rel="noopener noreferrer"&gt;platform hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Procfile
&lt;/h2&gt;

&lt;p&gt;Beanstalk has a default run command of &lt;code&gt;npm start&lt;/code&gt; but we want to specify the port number so nginx is listening on the correct port. We can customise the start command using a Procfile in the project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: node_modules/.bin/next start -p $PORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information on the &lt;a href="https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/nodejs-configuration-procfile.html" rel="noopener noreferrer"&gt;Procfile&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the server type
&lt;/h2&gt;

&lt;p&gt;By default, Beanstalk uses &lt;code&gt;t3.micro&lt;/code&gt; EC2 servers. From my initial testing, the build time took too long and actually timed out the deployment.&lt;/p&gt;

&lt;p&gt;We can define the servers we want to use in our build with a config file. Create an &lt;code&gt;.ebextensions&lt;/code&gt; directory in your project root, then create a file called &lt;code&gt;next.config&lt;/code&gt;. The name doesn’t really matter but it’s handy to know these are settings that are useful for Next.js setup.&lt;/p&gt;

&lt;p&gt;Paste the below into the next.config file and save it. This will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the t4g.medium server as a minimum which has 4GB of RAM and 2 vCPUs. Feel free to try lower spec servers and see if it works for you.&lt;/li&gt;
&lt;li&gt;Set nginx to listen on port 3000&lt;/li&gt;
&lt;li&gt;Set the environment to launch a single instance. This will save on costs for this example, but for a production site you probably want a load balanced environment and multiple servers running.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;option_settings:

  # Min 4GB RAM per instance for Next
  # The next build times out with micro servers
  aws:ec2:instances:
    InstanceTypes: t4g.medium, t4g.large

  # Configure the port the proxy server (nginx) listens on
  # https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/nodejs-platform-proxy.html
  aws:elasticbeanstalk:application:environment:
    PORT: 3000

  # Launch a single EC2 instance without a load balancer
  aws:elasticbeanstalk:environment:
    EnvironmentType: SingleInstance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a beanstalk application
&lt;/h2&gt;

&lt;p&gt;Now we have our files in place we can create an application using the Elastic Beanstalk CLI. You need the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;AWS CLI&lt;/a&gt; and the &lt;a href="https://github.com/aws/aws-elastic-beanstalk-cli" rel="noopener noreferrer"&gt;Elastic Beanstalk CLI&lt;/a&gt;, as well as &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html" rel="noopener noreferrer"&gt;configuring single sign on&lt;/a&gt; for the AWS CLI.&lt;/p&gt;

&lt;p&gt;In the below examples we are using &lt;code&gt;my-aws-profile&lt;/code&gt; as the AWS Single Sign On profile for our AWS account.&lt;/p&gt;

&lt;p&gt;The application is the container that can have many environments, such as production, staging, etc.&lt;/p&gt;

&lt;p&gt;Let’s create a Beanstalk app called &lt;code&gt;example-next&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;eb init example-next --profile my-aws-profile 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should automatically recognise that you are using node.js from your project files. Select the version of node you would like to use and follow the instructions to either create a new key pair or use an existing key pair for SSH access.&lt;/p&gt;

&lt;p&gt;The process takes a few minutes to run to generate the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a beanstalk environment
&lt;/h2&gt;

&lt;p&gt;Once we have an application, we can then create an environment and deploy our app. The Elastic Beanstalk CLI will zip up your project and upload it to an AWS S3 bucket. As Elastic Beanstalk automatically runs &lt;code&gt;npm install&lt;/code&gt; we can remove the &lt;code&gt;node_modules&lt;/code&gt; directory from our project before running the create or deploy commands to make the upload smaller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eb create staging --profile my-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will zip up your code and upload it to the S3 bucket that was created when we initialised the environment. It will then create the resources it needs, along with an EC2 server.&lt;/p&gt;

&lt;p&gt;When the EC2 server is ready, it will then copy the code into the &lt;code&gt;staging&lt;/code&gt; directory, run &lt;code&gt;npm install&lt;/code&gt;, then the predeploy hook and then move it to the current folder and run the command in our Procfile. It may take a bit longer the first time this is run as it has to create the environment and the EC2 server first. It should be quicker when deploying updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opening the environment
&lt;/h2&gt;

&lt;p&gt;To see our running Next.js app, run the below and it should launch a new browser window for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eb open staging --profile my-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying updates
&lt;/h2&gt;

&lt;p&gt;Let’s say we change our app code and want to deploy the latest version to our existing staging environment. This can be done with the following command. You use &lt;code&gt;eb deploy&lt;/code&gt; rather than &lt;code&gt;eb create&lt;/code&gt; as the environment already exists. The &lt;code&gt;staging&lt;/code&gt; in the below command is the name of the environment you want to update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eb deploy staging --profile my-aws-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will take a few minutes to run as it will build your Next.js app on the EC2 server before deploying the build and starting the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding environment variables
&lt;/h2&gt;

&lt;p&gt;You can add environment variables using the web console or the CLI. When you update the environment variables it should run the &lt;code&gt;confighooks&lt;/code&gt; we set earlier. This means it will rebuild your app using the latest environment variables, deploy it and then start it up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/developer-code-NT1Q3GZVFI" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/morillo" rel="noopener noreferrer"&gt;Christina Morillo&lt;/a&gt; on &lt;a href="https://stocksnap.io" rel="noopener noreferrer"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>hosting</category>
      <category>javascript</category>
    </item>
    <item>
      <title>HTML is beautiful</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/html-is-beautiful-3mjp</link>
      <guid>https://dev.to/chrisrhymes/html-is-beautiful-3mjp</guid>
      <description>&lt;p&gt;Building a modern website can sometimes lead you to be so far separated from the end result that is sent to the user. Developers can end up focusing on building sites with component based frontend frameworks, fetching data from APIs and installing hundreds of npm dependencies. We can become more interested in writing great code in their chosen programming language than what we serve to the website visitors. How did we get so far away from writing HTML?&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML is simple
&lt;/h2&gt;

&lt;p&gt;It’s true. HTML is pretty simple. It’s a markup language with some simple tags to tell the browser how to render the content.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need a top level heading, put it in a &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;If you need a paragraph of text, put it in a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag.&lt;/li&gt;
&lt;li&gt;If you want an unordered list, put it in an &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tag with &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; for each list item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This has led some to question whether HTML is a programming language.&lt;/p&gt;

&lt;p&gt;Maybe that’s why developers feel the need to abstract their time away from HTML? Make it a bit more complicated to feel like they are doing something more technical? I don’t know?&lt;/p&gt;

&lt;p&gt;Anyway, check out the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements" rel="noopener noreferrer"&gt;HTML elements reference&lt;/a&gt; for a full breakdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML is future proof
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://info.cern.ch/" rel="noopener noreferrer"&gt;first ever webpage&lt;/a&gt; written with HTML can still be viewed to this day. This displays that it is backwards compatible with modern browsers, but if you think about it the other way, it also means that it is future proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML is graceful
&lt;/h2&gt;

&lt;p&gt;There are different specifications for HTML, with HTML5 being the most recent, but HTML5 tags will degrade gracefully in older browsers. Unlike JavaScript which stops rendering when an error occurs, HTML will gracefully continue rendering the rest of the page.&lt;/p&gt;

&lt;p&gt;For example, if the old browser finds an article or section tag then it will be &lt;a href="https://www.pietschsoft.com/post/2010/11/14/html5-day-1-new-tags-work-in-older-browsers-awesome" rel="noopener noreferrer"&gt;rendered as a span&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On a side node, HTML5 still feels like a new thing to me, but after looking it up I discovered it was first released in January 2008!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;HTML5 was first released in a public-facing form on 22 January 2008, with a major update and “W3C Recommendation” status in October 2014.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/HTML5" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/HTML5&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML is responsive
&lt;/h2&gt;

&lt;p&gt;If you have been working in web development for a few years (ok, quite a few years) you may remember creating fixed width layouts with tables, then maybe using divs, then having to build mobile specific versions of websites, then responsive websites where the same website adapts to the device and screen size that is being used to view the website.&lt;/p&gt;

&lt;p&gt;Haven’t we come a long way!&lt;/p&gt;

&lt;p&gt;Well, no not really, because if you think about it, HTML with no CSS was already responsive. Content in a paragraph tag automatically wraps to the screen size. Developers forcing content into a fixed width layout broke HTMLs default flowing nature.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML is beautiful
&lt;/h2&gt;

&lt;p&gt;If we want the user to click on something on the page to submit a form, then we could create a div and give it some styles to make it square with a fancy border and some shadow, then add a JavaScript listener so we can perform an action to submit the form when the user clicks it.&lt;/p&gt;

&lt;p&gt;Or, we could use an input with type ‘submit’ that does all that for us.&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;form&amp;gt;&lt;/span&gt;
  // More form content
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this day and age of React components and endless frontend build complexities and npm dependencies, it’s sometimes easy to forget what HTML can already do for you out the box.&lt;/p&gt;

&lt;p&gt;Keep things simple where you can and make the most of built in and widely supported features where possible.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Fixing a few SEO issues with my author website</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Sat, 23 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/fixing-a-few-seo-issues-with-my-author-website-o2n</link>
      <guid>https://dev.to/chrisrhymes/fixing-a-few-seo-issues-with-my-author-website-o2n</guid>
      <description>&lt;p&gt;When I launched my cozy mystery series, The Little-Astwick Mysteries, I decided to create a new website to promote it. But I made a few mistakes with SEO that have led to a few issues with Search Engine Optimisation (SEO). Here is how I fixed them.&lt;/p&gt;

&lt;h2&gt;
  
  
  www or non-www subdomain
&lt;/h2&gt;

&lt;p&gt;The site is built using Jekyll, which has a &lt;code&gt;_config.yml&lt;/code&gt; file where you can specify your &lt;code&gt;site_url&lt;/code&gt; that is used to build all the links and the sitemap. I set the site_url to &lt;a href="https://www.littleastwick.co.uk" rel="noopener noreferrer"&gt;www.littleastwick.co.uk&lt;/a&gt;, but the configuration in Netlify was set to use littleastwick.co.uk (without the www) as the primary domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  What was the impact?
&lt;/h3&gt;

&lt;p&gt;A simple mistake, but it meant lots of redirects from the www addresses to non-www addresses and a conflict between the generated sitemap file and the pages being served to visitors.&lt;/p&gt;

&lt;p&gt;Search engines see each sub domain as a separate site. So &lt;a href="http://www.littleastwick.co.uk" rel="noopener noreferrer"&gt;www.littleastwick.co.uk&lt;/a&gt; is a different site to littleastwick.co.uk.&lt;/p&gt;

&lt;p&gt;In essence, I was telling search engines a list of urls on one subdomain, but when Google tried to crawl them it would be redirected to another domain (the non www domain).&lt;/p&gt;

&lt;h3&gt;
  
  
  How was this fixed?
&lt;/h3&gt;

&lt;p&gt;I host my site with Netlify. This was fixed by updating the ‘Domains configuration’ in Netlify to set the www address as the primary domain. Netlify now directed visitors to the pages with the www prefix and the sitemaps matched the subdomain correctly.&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%2F8ldss27egq49wpt0uapj.jpg" 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%2F8ldss27egq49wpt0uapj.jpg" alt="Netlify domain configuration" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, this is just for Netlify. The way to resolve this will vary depending on your hosting provider. Some have easy to use settings, whereas others may require you to edit configuration files. You may also need to update DNS (Domain Name System) settings to fix this issue, so check with your provider or speak to a developer, before you change anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfalls avoided
&lt;/h3&gt;

&lt;p&gt;The other saving grace with this was that Netlify is clever enough to only serve the site on one of the domains. If it was served on both then Google may have seen both sites and listed them separately in the search results or thought the site was duplicating content on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permalinks
&lt;/h2&gt;

&lt;p&gt;Jekyll has the option for you to set the &lt;a href="https://jekyllrb.com/docs/permalinks/" rel="noopener noreferrer"&gt;permalinks&lt;/a&gt; of your pages. This is the format of the url for the generated page. For blog posts it could have the year, month and day of the post in the url. For normal pages it defines if the page should end in a forward slash &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;index.html&lt;/code&gt; or &lt;code&gt;custom-page-name.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I didn’t set a value for the permalinks thinking it all worked and was ok with whatever defaults Jekyll decided to use.&lt;/p&gt;

&lt;p&gt;What I didn’t realise was that Netlify allowed you to visit the url ending in .html, but also visiting the page without the .html, or the url that ended in a forward slash. Some pages in the site directed visitors to the page without the .html, but the sitemap pointed search engines to pages with the .html.&lt;/p&gt;

&lt;h3&gt;
  
  
  What was the impact?
&lt;/h3&gt;

&lt;p&gt;The result of this was lots of pages with duplicate content. Many pages had canonical urls redirecting search engines to the page with .html. A canonical url is a way of telling search engines what the true source should be and what you prefer to be shown in search results.&lt;/p&gt;

&lt;h3&gt;
  
  
  How was this fixed?
&lt;/h3&gt;

&lt;p&gt;My resolution to this was to add &lt;code&gt;permalinks: pretty&lt;/code&gt; to the &lt;code&gt;_config.yml&lt;/code&gt; file. This meant the links in the built site and the sitemap now all end in a forward slash instead of .html.&lt;/p&gt;

&lt;p&gt;I am fully expecting there to be some fall out of this in the short term as search engines will have to reindex the site again with the updated urls, but hopefully it should resolve these issues in the long term.&lt;/p&gt;

&lt;h3&gt;
  
  
  Potential pitfalls avoided
&lt;/h3&gt;

&lt;p&gt;The bonus is that if someone visits an old link with the .html suffix, then it redirects the page to the new permalink ending with the forward slash suffix. Search engines should see these redirects and hopefully update their indexes over time to use the updated url.&lt;/p&gt;

&lt;h2&gt;
  
  
  404 page
&lt;/h2&gt;

&lt;p&gt;Whilst I was testing all this out, I also realised that I didn’t have a 404 page for my site.&lt;/p&gt;

&lt;h3&gt;
  
  
  What was the impact?
&lt;/h3&gt;

&lt;p&gt;If someone had visited a broken link, there wasn’t any links for them to easily get to where they needed to be or to easily get to the homepage, just a standard Netlify error page.&lt;/p&gt;

&lt;h3&gt;
  
  
  How was this fixed?
&lt;/h3&gt;

&lt;p&gt;This was resolved by creating a new custom &lt;code&gt;404.html&lt;/code&gt; page that used the default page layout. This meant it had the header and footer links to be able to navigate to other pages on the site and it had a message stating that it was a 404 error.&lt;/p&gt;

&lt;p&gt;There are different settings needed depending on your hosting, so check out the &lt;a href="https://jekyllrb.com/tutorials/custom-404-page/" rel="noopener noreferrer"&gt;custom 404 documentation&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Hopefully these changes should lead to both a better user experience and a better configured site for search engines to be able to determine what the actual url should be.&lt;/p&gt;

&lt;p&gt;I’m going to keep my eyes on the &lt;a href="https://search.google.com/search-console/about" rel="noopener noreferrer"&gt;Google Search Console&lt;/a&gt; over the next few weeks and see what other items are raised. If you haven’t yet set up Google Search Console, then I recommend you do. It has performance reports to show you how visitors are finding your site, but it also has really useful reports for potential SEO issues with pages it finds.&lt;/p&gt;

&lt;p&gt;Ensure you add your sitemap.xml file (create one if you don’t have a sitemap yet) to Google Search Console so it can easily find all your site’s pages and be informed when you create a new page too.&lt;/p&gt;

&lt;p&gt;I may need to add some manual redirects from old urls to new urls, but luckily there is a &lt;a href="https://github.com/jekyll/jekyll-redirect-from" rel="noopener noreferrer"&gt;Jekyll redirect from plugin&lt;/a&gt; that I can use for that.&lt;/p&gt;

&lt;p&gt;What I have learned is to spend more time on the configuration details. This site is quite small and does not many pages, but making these changes on a larger site could result in a much bigger impact to its search rankings and take longer to resolve.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>author</category>
      <category>seo</category>
    </item>
    <item>
      <title>Creating a custom toggle in TailwindCSS</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 29 Jul 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/creating-a-custom-toggle-in-tailwindcss-1h5m</link>
      <guid>https://dev.to/chrisrhymes/creating-a-custom-toggle-in-tailwindcss-1h5m</guid>
      <description>&lt;p&gt;I’ve only just started using TailwindCSS, (I know late to the party huh), and I wanted to create a custom toggle switch that looked a bit nicer than a standard checkbox. This blog post goes through some of the thought processes and the tools that Tailwindcss v4 has out of the box that you can make use of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with the Checkbox
&lt;/h2&gt;

&lt;p&gt;As I said, I wanted a checkbox to look nice, but what was really important to me was that it could still be used as a checkbox so it would be usable with a keyboard and work with Livewire, so when the state was updated the page would also update.&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;label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also wanted on and off labels to indicate what was changing when the checkbox was checked or unchecked.&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;label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Off label&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;On label&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic styles
&lt;/h2&gt;

&lt;p&gt;I have added some basic styles to make the labels appear side by side using flexbox, round the labels edges, and added the background colour of violet-700. The off label has a white background and the on label has a violet background to match the label background colour.&lt;/p&gt;

&lt;p&gt;Next we want to hide the checkbox from view, but still keep it in the page and make it visible to screen readers. We can use the &lt;a href="https://tailwindcss.com/docs/display#screen-reader-only" rel="noopener noreferrer"&gt;sr-only&lt;/a&gt; utility for this.&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;"inline-flex"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt;
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cursor-pointer flex flex-row rounded-full bg-violet-700 px-2 py-2"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sr-only"&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;"mr-2 inline-flex cursor-pointer rounded-full bg-white px-4 py-2 text-black"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Off label&lt;span class="nt"&gt;&amp;lt;/span&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;"inline-flex cursor-pointer rounded-full bg-violet-700 px-4 py-2 text-white"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;On label&lt;span class="nt"&gt;&amp;lt;/span&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/label&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;h2&gt;
  
  
  Using peer
&lt;/h2&gt;

&lt;p&gt;I need the style of the labels to change based on the state of the checkbox. So if the checkbox was checked then the ‘On label’ should be prominent, but if it was unchecked then I wanted the ‘Off label’ to be prominent.&lt;/p&gt;

&lt;p&gt;Tailwind has a &lt;a href="https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-sibling-state" rel="noopener noreferrer"&gt;peer&lt;/a&gt; utility that allows you to style based on a sibling’s state. This was perfect for my needs.&lt;/p&gt;

&lt;p&gt;We can add &lt;code&gt;peer&lt;/code&gt; to the checkbox class:&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;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"peer sr-only"&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;Then we can add the styles for the checked state for the off label using&lt;code&gt;peer-checked:&lt;/code&gt; to swap the text colour and background colours so it has a violet background and white text when the checkbox is checked.&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;span&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-2 inline-flex cursor-pointer rounded-full bg-white px-4 py-2 text-black peer-checked:bg-violet-700 peer-checked:text-white"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Off label
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we do the opposite for the on label by making the background white and the text black when the checkbox is checked.&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;span&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"inline-flex cursor-pointer rounded-full bg-violet-700 px-4 py-2 text-white peer-checked:bg-white peer-checked:text-black"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  On label
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we click to check and uncheck the checkbox, the highlighted label will also update to reflect whether the checkbox is checked or not.&lt;/p&gt;

&lt;p&gt;We can also use the keyboard to focus on the checkbox using tab, then use space bar to check and uncheck the checkbox input, changing the label appearance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding an outline with focus-within
&lt;/h2&gt;

&lt;p&gt;It’s always useful to indicate the focus state of an input, especially for keyboard users. But in our case we have hidden the checkbox, except for screen readers so how can we show the element is focused?&lt;/p&gt;

&lt;p&gt;Again, Tailwindcss has you covered with the &lt;a href="https://tailwindcss.com/docs/hover-focus-and-other-states#focus-within" rel="noopener noreferrer"&gt;focus-within&lt;/a&gt; utility that lets you apply styles to a parent element when the focus state is within the parent.&lt;/p&gt;

&lt;p&gt;We can add an amber outline by using &lt;code&gt;focus-within:&lt;/code&gt; on the outer label.&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;label&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cursor-pointer flex flex-row rounded-full bg-violet-700 px-2 py-2 focus-within:outline-4 focus-within:outline-amber-400"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Input content here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we focus the state by clicking with the mouse or selecting the checkbox with the keyboard then the outline appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a transition
&lt;/h2&gt;

&lt;p&gt;To make the transition between the on and off states a bit smoother and animated we can use Tailwind’s &lt;a href="https://tailwindcss.com/docs/transition-property" rel="noopener noreferrer"&gt;transition&lt;/a&gt; utility and add it to both spans.&lt;/p&gt;

&lt;p&gt;Here we add &lt;code&gt;transition-all duration-700 ease-in-out&lt;/code&gt; to tell tailwind to transition all items with a duration of 700ms and using ease-in-out transition timing feature.&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;span&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mr-2 inline-flex cursor-pointer rounded-full bg-white px-4 py-2 text-black transition-all duration-700 ease-in-out peer-checked:bg-violet-700 peer-checked:text-white"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Off label
&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final example
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/chrisrhymes/embed/KwdNZMy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Further improvements
&lt;/h2&gt;

&lt;p&gt;This focuses purely on the styling, but I think further work would be needed to make this fully accessible by adding additional aria attributes to indicate to users the current state of the checkbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/night-cityscape-7AVEXUYJHQ" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/candacemcdaniel" rel="noopener noreferrer"&gt;Candace McDaniel&lt;/a&gt; on &lt;a href="https://stocksnap.io" rel="noopener noreferrer"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Testing window.open() in JavaScript with Jest</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Sun, 01 Dec 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/testing-windowopen-in-javascript-with-jest-2i87</link>
      <guid>https://dev.to/chrisrhymes/testing-windowopen-in-javascript-with-jest-2i87</guid>
      <description>&lt;p&gt;I recently had to write a test for a React component that opened a new browser window. To open the new window I made use of window.open() in my code. This made the component easy to write, but I had to think a bit differently about how to write the test for this.&lt;/p&gt;

&lt;p&gt;More information on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/open" rel="noopener noreferrer"&gt;the window.open() method&lt;/a&gt; is available on mdn web docs.&lt;/p&gt;

&lt;p&gt;To set a bit or background, I had a React component that had a simple form with a couple of inputs. When the user completed the inputs and submitted the form it opened a new window to a specified URL with the inputs as URL parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The component to test
&lt;/h2&gt;

&lt;p&gt;Here is a very simplified version of the component as a demonstration. I’d recommend using something like &lt;a href="https://www.react-hook-form.com/" rel="noopener noreferrer"&gt;react-hook-form&lt;/a&gt; to add validation to your form.&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;// MyForm.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSubject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?name=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;subject=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;subject&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Name&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Subject&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"subject"&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"subject"&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Submit (opens in new window)"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have our component, lets think about the test for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’d normally test
&lt;/h2&gt;

&lt;p&gt;Normally I would test what has been rendered in my component, using assertions such as expect the component to have text content or assert the url is what is expected (using window.location.href), but I quickly realised that approach won’t work in jest for this example.&lt;/p&gt;

&lt;p&gt;Window.open opens a new browser window, so it doesn’t affect the component we are testing. We can’t see what is inside the new window or what its url is as it is outside of the scope of the component we are testing.&lt;/p&gt;

&lt;p&gt;So how do we test something that is outside of what we can see? We don’t actually need to test that a new window is opened as that would be testing the window interface’s functionality and not our code. Instead, we just need to test that the window.open method is called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocking window.open()
&lt;/h2&gt;

&lt;p&gt;Therefore we need to mock window.open() and test that it was called inside our code.&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;// Mock window.open&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can set the values in the inputs, submit our form and then test that the window.open was called. We can use fireEvent to set the values of the inputs and pressing the submit button.&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="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Name&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="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An example subject&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="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Submit (opens in new window)&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;It’s worth having a read through the documentation for the &lt;a href="https://testing-library.com/docs/guide-events" rel="noopener noreferrer"&gt;considerations for fireEvent&lt;/a&gt;. You may want to use user-event instead depending on your use case.&lt;/p&gt;

&lt;p&gt;We want to await for the method to run. We can do that using &lt;a href="https://testing-library.com/docs/dom-testing-library/api-async/#waitfor" rel="noopener noreferrer"&gt;waitFor()&lt;/a&gt;.&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;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&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;To ensure we are not opening loads of new windows, we can check that we only call window.open once.&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;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledTimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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 also check what arguments the method is called with, passing in the URL we expect as the first argument and the target as the second.&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;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com?name=Test%20Name&amp;amp;subject=An%20example%20subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&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;h2&gt;
  
  
  The complete test file
&lt;/h2&gt;

&lt;p&gt;Here is the complete test file for your reference.&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;// MyForm.test.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitFor&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="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./MyForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyForm test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Mock window.open&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;opens a new window with the correct url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyForm&lt;/span&gt; &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://example.com"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test Name&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="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An example subject&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="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Submit (opens in new window)&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;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledTimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com?name=Test%20Name&amp;amp;subject=An%20example%20subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stocksnap.io/photo/laptop-apple-UOI2HF8SXU" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/41320" rel="noopener noreferrer"&gt;energepic.com&lt;/a&gt; on &lt;a href="https://stocksnap.io" rel="noopener noreferrer"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Adding social icons to the Bulma Clean Theme footer</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 03 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/adding-social-icons-to-the-bulma-clean-theme-footer-1c79</link>
      <guid>https://dev.to/chrisrhymes/adding-social-icons-to-the-bulma-clean-theme-footer-1c79</guid>
      <description>&lt;p&gt;Version 1.1.0 of Bulma clean theme has been released. It has a small update that allows you to easily add social media links to the footer of your site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Font Awesome update
&lt;/h2&gt;

&lt;p&gt;It sounds like a small feature update, but I wanted to create a blog post about it as it required updating to Font Awesome v6 to get the latest social media icons.&lt;/p&gt;

&lt;p&gt;When upgrading to v1.1.0 of the theme, please ensure you take a read through the changes in Font Awesome and see if your site is affected by any of the changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.fontawesome.com/web/setup/upgrade/whats-changed" rel="noopener noreferrer"&gt;Font Awesome 6 - What’s changed?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding social links to the footer
&lt;/h2&gt;

&lt;p&gt;To add social media links to the footer you need to add a social key to your &lt;code&gt;_config.yml&lt;/code&gt; and add the relevant social network names with links to your profiles.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;social&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;facebook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.facebook.com/&lt;/span&gt;
  &lt;span class="na"&gt;instagram&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.instagram.com/&lt;/span&gt;
  &lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.threads.net/&lt;/span&gt;
  &lt;span class="na"&gt;tiktok&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.tiktok.com/&lt;/span&gt;
  &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.x.com/&lt;/span&gt;
  &lt;span class="na"&gt;youtube&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.youtube.com/&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For more information take a look at the &lt;a href="https://www.csrhymes.com/bulma-clean-theme/docs/navigation/footer-navigation/#footer-social-links" rel="noopener noreferrer"&gt;theme documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jekyll</category>
      <category>theme</category>
      <category>bulma</category>
    </item>
    <item>
      <title>Building a landing page for your book</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Sat, 15 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/building-a-landing-page-for-your-book-3e25</link>
      <guid>https://dev.to/chrisrhymes/building-a-landing-page-for-your-book-3e25</guid>
      <description>&lt;p&gt;This post follows on from my last post about using your website to promote your ebooks. The first step of the article explains that you need to make a website, but didn’t go into too much detail. This post aims at explaining how you can build a landing page for your book with Bulma Clean Theme.&lt;/p&gt;

&lt;p&gt;I’ve recently added a new promo page layout which can be used to build a landing page for your book. You can see a demo page here.&lt;/p&gt;

&lt;p&gt;To make it easier to get started I have created a repository that you can fork to get started. Don’t worry if you don’t understand the jargon right now, we will walk through the set up step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a GitHub account.
&lt;/h2&gt;

&lt;p&gt;If you don’t have a GitHub account then you can sign up for one for free. From the &lt;a href="https://github.com/"&gt;github.com&lt;/a&gt; homepage, press the Sign up button on the top right and follow the instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forking the repository
&lt;/h2&gt;

&lt;p&gt;Rather than starting from scratch, I have create a starter repository that you can use to build from.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://github.com/chrisrhymes/example-promo-page"&gt;example-promo-page starter repository&lt;/a&gt; and click on the &lt;code&gt;Fork&lt;/code&gt; button towards the top right of the page.&lt;/p&gt;

&lt;p&gt;Give your fork a name and press &lt;code&gt;Create fork&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making changes
&lt;/h2&gt;

&lt;p&gt;With GitHub you normally clone the code, edit your content on your computer and then use git to commit your changes and push them back up to GitHub. Don’t worry. To simplify the process, we are going to use the ‘Edit in github.dev’ editor.&lt;/p&gt;

&lt;p&gt;To start the GitHub.dev editor, press &lt;code&gt;.&lt;/code&gt; whilst viewing your fork in GitHub. More information is available on the &lt;a href="https://docs.github.com/en/codespaces/the-githubdev-web-based-editor"&gt;GitHub docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should see an editor appear with the list of files on the left of the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edit the configuration
&lt;/h2&gt;

&lt;p&gt;The site wide settings are stored in the &lt;code&gt;_config.yml&lt;/code&gt; file. Edit the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;title - set this to your author pen name&lt;/li&gt;
&lt;li&gt;description - set this as a description of your website.&lt;/li&gt;
&lt;li&gt;base_url - set this to &lt;code&gt;""&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add images
&lt;/h2&gt;

&lt;p&gt;Go to the img folder and add an image for your avatar. A square cropped image about 400px wide by 400px high should work well here. In this tutorial we will call the image &lt;code&gt;author.jpg&lt;/code&gt;. Drag and drop the author image into the img folder.&lt;/p&gt;

&lt;p&gt;Now add an image for your book cover. An image cropped to a 800px wide by 1277px high. This is a 2 by 3 image ratio that seems to work well for book covers. Once you have your cropped image you can drag and drop this into the img folder too. Let’s call this image &lt;code&gt;book-cover.jpg&lt;/code&gt; for this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the index.md page
&lt;/h2&gt;

&lt;p&gt;The index.md page is your homepage. There is a section called front matter that is between the two sets of &lt;code&gt;---&lt;/code&gt;. These are the settings and configuration for the page. Below the second &lt;code&gt;—--&lt;/code&gt; is the page content that is used for the about section.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the title and subtitle for your book. Use the title for your book name and then the subtitle as your author pen name.&lt;/li&gt;
&lt;li&gt;Update the hero_image to the path to your book cover image file that you added to the img folder. As we named it book-cover.jpg, we need to set this to &lt;code&gt;/img/book-cover.jpg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update the hero_image_alt text to the name of your book.&lt;/li&gt;
&lt;li&gt;Update the hero_link to the link to where you sell your book, such as the amazon store page.&lt;/li&gt;
&lt;li&gt;Update the snippet text. This should be a short description of your book.&lt;/li&gt;
&lt;li&gt;Update the about_image to our author image, so we need to set it to &lt;code&gt;/img/author.jpg&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Edit the content of the page (below the second &lt;code&gt;---&lt;/code&gt;) and write about yourself and your books. This content is written in &lt;a href="https://www.markdownguide.org/basic-syntax/"&gt;markdown format&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For full instructions on this page take a look through the documentation for &lt;a href="https://www.csrhymes.com/bulma-clean-theme/docs/promo-pages/creating-a-promo-page/"&gt;creating a promo page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reviews
&lt;/h2&gt;

&lt;p&gt;To add a few reviews, edit the &lt;code&gt;_data/reviews/book1.yml&lt;/code&gt; file. More information is available on the &lt;a href="https://www.csrhymes.com/bulma-clean-theme/docs/products/product-reviews/"&gt;reviews documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Newsletter
&lt;/h2&gt;

&lt;p&gt;To add a newsletter form, edit the &lt;code&gt;_includes/newsletter.html&lt;/code&gt; file. This file has a placeholder form that doesn’t do anything. Copy the code from your form provider for your mailing list, such as Mailchimp, and paste it over the form in the newsletter.html file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving changes
&lt;/h2&gt;

&lt;p&gt;Ensure that you have saved all your changes on each individual file by using &lt;code&gt;cmd + s&lt;/code&gt; for Mac or &lt;code&gt;ctrl + s&lt;/code&gt; for Windows.&lt;/p&gt;

&lt;p&gt;Next, go to the source control tab on the left menu. You should see a list of the files that have been changed. Add a message into the top input box, something like ‘Initial setup’ and then press the &lt;code&gt;Commit &amp;amp; Push&lt;/code&gt; button. This will commit your changes to your GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying your site
&lt;/h2&gt;

&lt;p&gt;We are now ready to deploy your site. It’s up to you where you host your site but I would recommend a service that integrates with GitHub which will automatically deploy your changes each time you &lt;code&gt;Commit &amp;amp; Push&lt;/code&gt; to GitHub. One such service is netlify, but there are a range of options available. Take a look at the &lt;a href="https://jekyllrb.com/docs/deployment/third-party/"&gt;Jekyll deployment documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain Name
&lt;/h2&gt;

&lt;p&gt;Hopefully you now have a working promo page for your new book. The next thing to do is get your DNS set up. How you do this depends on your choice of hosting, so take a look at the service you have chosen for getting the domain name set up.&lt;/p&gt;

&lt;p&gt;Alternatively, work with a web developer to help get this configured for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;As stated at the beginning of this article, the promo page is powered by Bulma Clean Theme and has all the built in functionality.&lt;/p&gt;

&lt;p&gt;It has a blog page and a demo post that you can use to start blogging. Take a read through the &lt;a href="https://jekyllrb.com/docs/posts/"&gt;Jekyll Posts documentation&lt;/a&gt; to get started with blogging.&lt;/p&gt;

&lt;p&gt;You can also &lt;a href="https://www.csrhymes.com/bulma-clean-theme/docs/getting-started/theming/#setting-the-primary-colour"&gt;customise the colour scheme&lt;/a&gt; to suit your branding requirements.&lt;/p&gt;

&lt;p&gt;Take a read through the &lt;a href="https://www.csrhymes.com/bulma-clean-theme/docs/"&gt;Bulma Clean Theme documentation&lt;/a&gt; to see all the options available.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>marketing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to promote your ebooks with your website?</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Thu, 06 Jun 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/how-to-promote-your-ebooks-with-your-website-2c46</link>
      <guid>https://dev.to/chrisrhymes/how-to-promote-your-ebooks-with-your-website-2c46</guid>
      <description>&lt;p&gt;I’m a web developer by trade and a part-time author, so here are a few things that I have done to help promote my books and ebooks using my website and my tech know how from my day job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a website
&lt;/h2&gt;

&lt;p&gt;If you don’t have a website, then start by building one. Easy huh?&lt;/p&gt;

&lt;p&gt;At the very least, start with a single landing page so you have a presence on the internet when people search for your name or book title. From here you can direct people to where your book is sold and your social media sites so people can be kept up to date.&lt;/p&gt;

&lt;p&gt;I built a website, this website in fact, partly to promote my books, partly to promote my work.&lt;/p&gt;

&lt;p&gt;This site is built using &lt;a href="https://github.com/chrisrhymes/bulma-clean-theme"&gt;Bulma-Clean-Theme&lt;/a&gt;, a Jekyll theme that includes product listing pages that you can use to promote your books. If you are interested in development then this may be a way for you to go, but if you aren’t then take a look at site builders or something in between such as WordPress.&lt;/p&gt;

&lt;p&gt;The biggest benefit to me of using Jekyll is that I can host it for free with &lt;a href="https://pages.github.com/"&gt;GitHub pages&lt;/a&gt; or &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;, whereas site builders normally come at a monthly cost. There is also no hassle with maintaining a web server as it is a static site. This means the HTML pages are generated into static HTML files and deployed somewhere, in my case GitHub Pages. The way I see it, if you are able to save some money on your website, then you have more to spend elsewhere.&lt;/p&gt;

&lt;p&gt;Do what works for you. Pick a solution that is easiest for you to maintain and update, that way, you won’t be put off from making regular updates to your site and it will become part of your routine. Updating little and often, rather than overhauling everything every couple of years, is generally better as return visitors will be encouraged to come back more often and see what is new.&lt;/p&gt;

&lt;h2&gt;
  
  
  Register a domain name
&lt;/h2&gt;

&lt;p&gt;Once you have your site up and running you need to register a domain name for your website and point it at your new website. Ensure that you own the domain name and not a third party, such as a developer or an agency.&lt;/p&gt;

&lt;p&gt;You also want to ensure that you can update your domain name settings so you can point it somewhere else in future, such as to a different website host, if you want to move your website. You don’t want to be stuck and have to buy a new domain in future and have to start all over again.&lt;/p&gt;

&lt;p&gt;Your domain should be concise and easy to type in the address bar. This makes it easier for return visitors to check back at a later date without having to rely on a search engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a blog
&lt;/h2&gt;

&lt;p&gt;I’ve found that a good way of getting visitors to your new site is to create a blog. Like this one! Most of the traffic to my website comes from the blog posts. Once people land on your site they can look around and see what else is on your site.&lt;/p&gt;

&lt;p&gt;You can always link to your &lt;a href="https://www.csrhymes.com/books"&gt;books&lt;/a&gt; from your blog post (see what I did there) which will help visitors to your blog post find your books.&lt;/p&gt;

&lt;p&gt;I have heard people say they want a blog, but then I ask them what are they going to write about. They create a single post and then never use it again. I’d suggest writing a list of possible blog post topics and pick the one that you are most excited to write about. If you are excited to write about it then others will be excited to read about it.&lt;/p&gt;

&lt;p&gt;Every time you have a new idea for a blog post, ensure you write it down somewhere, otherwise when it actually comes to writing the post, you will be sat in front of an empty screen whilst you try to remember what your great idea was. I use the notes app on my phone to keep my list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add interesting titles and meta description tags to your pages
&lt;/h2&gt;

&lt;p&gt;Spend some time thinking about the title and meta description for your page. These appear in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag and &lt;code&gt;&amp;lt;meta name=“description”&amp;gt;&lt;/code&gt; tag in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; part of the HTML. This is normally used by search engines results so this is what people that find your site see, so it needs to encourage people to click on that result instead of one of the other links.&lt;/p&gt;

&lt;p&gt;Take a look at some best practices for descriptions on the &lt;a href="https://developers.google.com/search/docs/appearance/snippet#meta-descriptions"&gt;Google Search documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tile is also used when sharing your link on social media sites. You can go one step further for social media and add additional tags specific to Facebook and X (formerly Twitter), known as &lt;a href="https://ogp.me/"&gt;OpenGraph&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I use the Jekyll SEO tag plugin to automatically generate the meta tags for my site, but there are also plugins for WordPress to help you generate these tags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell Google and other search engines
&lt;/h2&gt;

&lt;p&gt;You may be lucky and a search engine may stumble upon your website, but why not give it a head start and tell search engines it exists. This can be done through &lt;a href="https://search.google.com/search-console/about"&gt;Google Search Console&lt;/a&gt;. You provide the url of your site and then verify that you own it, either through adding a meta tag to the code, uploading a file, through Google Tag manager or Google Analytics, or by adding a DNS record. Sounds complicated, but it’s definitely worth doing.&lt;/p&gt;

&lt;p&gt;Once you have verified you own the site, you can then submit your sitemap. The sitemap tells Google about all of your site’s pages so it knows they exist and can then crawl the pages and then display them in search results.&lt;/p&gt;

&lt;p&gt;So what is a &lt;a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap"&gt;sitemap&lt;/a&gt;? It’s an XML file in a particular format that provides a list of your websites pages. Jekyll and WordPress have plugins to create a sitemap for you so you don’t have to manually write out a load of XML and keep it updated.&lt;/p&gt;

&lt;p&gt;Each time you add a new page, such as when a new book page or a new blog post is added, it is added to your sitemap and Google should then index the page next time it reads your sitemap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add your website address to your social media profiles
&lt;/h2&gt;

&lt;p&gt;People looking at your posts and profile will want to know more about you, so provide them with a link in your profile back to your website. This is especially useful if the social media site shows your public profile to search engines. Each link back to your site helps boost its rankings in search engines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your blog posts on social media
&lt;/h2&gt;

&lt;p&gt;When you have spent so much time writing a blog post, ensure you share it with others. One thing that caught me out, but is probably obvious to most users these days, is that some social sites don’t allow links. Instagram is one example of this. So maybe concentrate on X (formerly Twitter), Facebook, LinkedIn and Threads for sharing links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing to other sites
&lt;/h2&gt;

&lt;p&gt;I first got real traction for my blog posts after sharing to another site, who then promoted my blog post for me. In my case, the posts I tend to write are about my day job as a web developer using Laravel and JavaScript. I discovered Laravel News, which allows you to submit your links to their site.&lt;/p&gt;

&lt;p&gt;Approved links then get published to a &lt;a href="https://laravel-news.com/links"&gt;Community Links&lt;/a&gt; section on their site, but even more importantly, they also share the links on their social media profiles too, as well as sharing the links in a weekly email to their large subscriber base.&lt;/p&gt;

&lt;p&gt;Remember to pick sites that are relevant to your subject matter to share to, so don’t submit non Laravel related blog posts to Laravel News.&lt;/p&gt;

&lt;p&gt;I am still looking for a site that allows authors to share their blog posts with other authors and readers. If you are aware of one then please let me know!&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your blog’s RSS feed
&lt;/h2&gt;

&lt;p&gt;Most blogs allow you to generate an XML feed that can be read by other sites to provide links back to your site. Again, for Jekyll, there is a plugin that lets you create a feed for your posts and WordPress has it as a default feature.&lt;/p&gt;

&lt;p&gt;As a developer, I found &lt;a href="https://dev.to/chrisrhymes"&gt;dev.to&lt;/a&gt;, which allows you to import posts from your website’s feed. The added bonus of this is that it sets a tag telling search engines that the post was originally created on your website. This is called a &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls#rel-canonical-link-method"&gt;canonical link&lt;/a&gt; and helps tell search engines where the content originally came from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@chrisrhymes"&gt;Medium.com&lt;/a&gt; also allows you to import posts from my website’s RSS feed, again with a canonical link pointing back to your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell people about your site and your books
&lt;/h2&gt;

&lt;p&gt;Last but not least, tell people in real life. I have to admit I am terrible at this. I have found that your website and blog will quietly sit there unused unless you go out of your way to tell people about it.&lt;/p&gt;

&lt;p&gt;You should be proud of what you do and want others to read about it.&lt;/p&gt;

&lt;p&gt;Add your website address to your business cards, email signature, wherever you can think of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hope that helps
&lt;/h2&gt;

&lt;p&gt;Hopefully there are lots of ideas there to get you started and give your website and blog a boost to help give your books some more visibility.&lt;/p&gt;

&lt;p&gt;If you have found this article useful then check out “&lt;a href="https://dev.to/products/how-not-to-make-a-website"&gt;How NOT to make a Website&lt;/a&gt;” by C.S. Rhymes for more information on what not to do when building a website.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>marketing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using the GOV.UK Design System with Laravel</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Wed, 29 May 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/using-the-govuk-design-system-with-laravel-2g0i</link>
      <guid>https://dev.to/chrisrhymes/using-the-govuk-design-system-with-laravel-2g0i</guid>
      <description>&lt;p&gt;I recently worked on a project using the GOV.UK Design System with a Laravel project. The GOV.UK frontend provides layouts and accessible HTML components with their own CSS and JavaScript. The two packages worked really well together, so I thought I would provide a quick example of how to get it setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to consider
&lt;/h2&gt;

&lt;p&gt;Before you use the GOV.UK frontend you must consider the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You’re welcome to use the template even if your service isn’t considered part of GOV.UK, but your site or service must not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;identify itself as being part of GOV.UK&lt;/li&gt;
&lt;li&gt;use the crown or GOV.UK logotype in the header&lt;/li&gt;
&lt;li&gt;use the GDS Transport typeface&lt;/li&gt;
&lt;li&gt;suggest that it’s an official UK government website if it’s not&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/alphagov/govuk-frontend-docs/blob/main/README.md"&gt;GOV UK Frontend Readme&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a new project
&lt;/h2&gt;

&lt;p&gt;First we create a new Laravel project, let’s call it &lt;code&gt;gov-uk-laravel-demo&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;composer create-project laravel/laravel gov-uk-laravel-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install npm dependencies
&lt;/h2&gt;

&lt;p&gt;Then we change into the directory and install our npm dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd gov-uk-laravel-demo
npm install
npm install govuk-frontend --save
npm install sass vite-plugin-static-copy —dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing the styles
&lt;/h2&gt;

&lt;p&gt;Rename &lt;code&gt;resources/css/app.css&lt;/code&gt; to &lt;code&gt;resources/scss/app.scss&lt;/code&gt; and add the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$govuk-assets-path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"/build/assets/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;"node_modules/govuk-frontend/dist/govuk/all"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sass variable is updating the default path from &lt;code&gt;/assets/&lt;/code&gt; as we are using vite, which puts everything inside a &lt;code&gt;build&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing the JavaScript
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;resources/js/app.js&lt;/code&gt;, update to the following to initialise the govuk-frontend JavaScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./bootstrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initAll&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="s2"&gt;govuk-frontend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;initAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring vite and building
&lt;/h2&gt;

&lt;p&gt;Update vite.config.js to build our scss files (previously css) and &lt;a href="https://frontend.design-system.service.gov.uk/import-font-and-images-assets/#copy-the-font-and-image-files-into-your-application"&gt;copy the fonts and images&lt;/a&gt; to the &lt;code&gt;public/build/assets&lt;/code&gt; folder using the viteStaticCopy plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&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="s2"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;laravel&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;laravel-vite-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;viteStaticCopy&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="s2"&gt;vite-plugin-static-copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;laravel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resources/scss/app.scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resources/js/app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;refresh&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="nf"&gt;viteStaticCopy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;targets&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;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules/govuk-frontend/dist/govuk/assets/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="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;Then run &lt;code&gt;npm run build&lt;/code&gt; to build the css, js and copy the relevant assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a page
&lt;/h2&gt;

&lt;p&gt;Copy and paste the &lt;a href="https://design-system.service.gov.uk/styles/page-template/"&gt;default page template HTML&lt;/a&gt; from the documentation page into the &lt;code&gt;welcome.blade.php&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Remove the script tags at the bottom of the 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="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/javascripts/govuk-frontend.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;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;initAll&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="s2"&gt;/javascripts/govuk-frontend.min.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;initAll&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;Then replace these two lines in the head:&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;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/assets/manifest.json"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/stylesheets/govuk-frontend.min.css"&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;With this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@vite(['resources/scss/app.scss', 'resources/js/app.js'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run php artisan serve and head to &lt;code&gt;http://localhost:8000&lt;/code&gt; to view our new page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What next
&lt;/h2&gt;

&lt;p&gt;From here you can start building your app by creating a layout blade component that other pages can reuse, then create reusable components following the HTML examples in the &lt;a href="https://design-system.service.gov.uk/components/"&gt;GOV.UK Design System&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/bigben-night-LWRWOL8KSV"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/negativespace"&gt;NegativeSpace&lt;/a&gt; on &lt;a href="https://stocksnap.io"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Customising Laravel Sail services using the Laravel Build server</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 21 May 2024 12:00:07 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/customising-laravel-sail-services-using-the-laravel-build-server-48g7</link>
      <guid>https://dev.to/chrisrhymes/customising-laravel-sail-services-using-the-laravel-build-server-48g7</guid>
      <description>&lt;p&gt;Laravel offers a quick and easy command to create a new Laravel project that uses Laravel Sail using the Laravel build server. But what if you want to customise what services are installed by default?&lt;/p&gt;

&lt;h2&gt;
  
  
  The default script
&lt;/h2&gt;

&lt;p&gt;To create a default Laravel build using Docker with Laravel Sail on macOS you can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; “https://laravel.build/example-app” | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new Laravel app in the folder called ‘example-app’ with the following services installed by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Meilisearch&lt;/li&gt;
&lt;li&gt;Mailpit&lt;/li&gt;
&lt;li&gt;Selenium&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;a href="https://laravel.com/docs/11.x#sail-on-macos"&gt;Laravel Documentation&lt;/a&gt; for more information and for how to get started with other operating systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why customise?
&lt;/h2&gt;

&lt;p&gt;The default list of services is probably fine for most use cases, but for one project I was building an API without any web interface. I wanted to streamline the services to speed up the initial set up and remove the services I wasn’t planning on using in the app.&lt;/p&gt;

&lt;p&gt;In this case I didn’t need Meilisearch, Mailpit or Selenium in my app. I didn’t need Selenium as I had no interface to test with Laravel Dusk. It also didn’t have a search or the need to send any emails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing services after the install
&lt;/h2&gt;

&lt;p&gt;One way I found to manually remove the services, is to run the default script and then before you run sail up, then manually edit the docker-composer.yml file, removing the services you don’t need. However, this means you have still downloaded docker images that you don’t need, taking time and taking up storage space on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing unneeded Sail services by editing the script
&lt;/h2&gt;

&lt;p&gt;We can modify the default script by adding a with query parameter with a comma separated list of the services we need.&lt;/p&gt;

&lt;p&gt;From our example above we only want MySQL and Redis to be setup so we can achieve this by using the following script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; “https://laravel.build/example-app?with&lt;span class="o"&gt;=&lt;/span&gt;mysql,redis” | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we open up our docker-compose.yml file we can see our PHP container (laravel.test), along with MySQL and Redis. The other services are no longer there as desired.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding additional services at install time
&lt;/h2&gt;

&lt;p&gt;We can also add additional services by adding them to the with query parameter comma separated list. For example, the below script will add Memcached to the default services installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; “https://laravel.build/example-app?with&lt;span class="o"&gt;=&lt;/span&gt;mysql,redis,meilisearch,mailpit,selenium,memcached” | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding services after install
&lt;/h2&gt;

&lt;p&gt;If you want to add an additional service after you have run the script, such as Mailpit to preview emails, then you can use the &lt;code&gt;sail:add&lt;/code&gt; artisan command from your project root to add a service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan sail:add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will provide a list of available services to choose from. Select from the list and it will add it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;I took a look at the &lt;a href="https://github.com/laravel/sail-server/blob/master/routes/web.php"&gt;Laravel sail server repo in GitHub&lt;/a&gt; to figure out how to customise the services installed. It allows you to set the ‘with’ and the ‘php’ query parameters to define the services and the php version required.&lt;/p&gt;

&lt;p&gt;If you do provide a with query parameter then it will use that otherwise it defaults to &lt;code&gt;mysql,redis,meilisearch,mailpit,selenium&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It also validates the list and ensures that the service you specify is within the list of available services. For example, if you try to add &lt;code&gt;nginx&lt;/code&gt; to the list of services then it will return a validation error message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://laravel.build/test-build?with=mysql,redis,nginx"&lt;/span&gt; | bash
bash: line 1: syntax error near unexpected token &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'
bash: line 1: `Invalid service name. Please provide one or more of the supported services (mysql, pgsql, mariadb, redis, memcached, meilisearch, typesense, minio, mailpit, selenium, soketi) or "none".'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stocksnap.io/photo/cranes-construction-KOKXBB635S"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/wyncliffe"&gt;Wyncliffe&lt;/a&gt; on &lt;a href="https://stocksnap.io"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>docker</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Getting started with React Hook Form</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Fri, 10 May 2024 18:00:07 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/getting-started-with-react-hook-form-4af8</link>
      <guid>https://dev.to/chrisrhymes/getting-started-with-react-hook-form-4af8</guid>
      <description>&lt;p&gt;I normally develop forms in Laravel, using Livewire where possible. Laravel has some great form validation tools built in that I’m really used to working with and Livewire offers easy to use state management. For this project though, I had to build the form in a React project.&lt;/p&gt;

&lt;p&gt;I looked for a package that would provide the validation and state management that I was used to but for React. After a bit of searching I found the &lt;a href="https://react-hook-form.com/"&gt;React Hook Form&lt;/a&gt; package that seemed to offer exactly what I was after. The tagline on their website reads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Performant, flexible and extensible forms with easy-to-use validation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was easy to install using npm:&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 react-hook-form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I could start using it in my form.&lt;/p&gt;

&lt;h2&gt;
  
  
  The example form
&lt;/h2&gt;

&lt;p&gt;The example form has a text input that is used to enter an email address. The form will submit and just console log out the data for this demo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SubmitHandler&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="s2"&gt;react-hook-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyFormInputs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Email&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;register&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot going on here, but let’s start with the interface &lt;code&gt;MyFormInputs&lt;/code&gt;. This is where you define the fields for your form, and what type they are. In our example we have Email which is a string.&lt;/p&gt;

&lt;p&gt;Once we define our interface, we can then pass it into the useForm so that knows what fields are in your form.&lt;/p&gt;

&lt;p&gt;In this example we are passing in an empty object into useForm, but there are some handy options that can be defined here if you want to, such as setting default values. Here we could set a default value for our email field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;defaultValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at the &lt;a href="https://react-hook-form.com/docs/useform"&gt;API documentation for useForm&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;When the form is submitted it will call the handleSubmit method from useForm, which will then call our custom onSubmit method that console logs the data. If we press the submit button we will get an object with the key ‘Email’ and the data we typed into the input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlled components
&lt;/h2&gt;

&lt;p&gt;I already had some form components in my project that I wanted to reuse as it would save me a lot of time and reduce risk of duplicating code. React Hook Form allows you to use a &lt;a href="https://react-hook-form.com/get-started#IntegratingControlledInputs"&gt;Controller component&lt;/a&gt; that you can use to render your controlled inputs.&lt;/p&gt;

&lt;p&gt;In the code below I am wrapping the TextInput component inside the render method of the Controller. I can then pass in the value and the onChange method to the controlled input and React Hook Form manages the state changes for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SubmitHandler&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="s2"&gt;react-hook-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyFormInputs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Email&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;control&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Controller&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"TextField"&lt;/span&gt;
        &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We set the name of the controller to the name of our field ‘Email’ and pass in the control from the useForm. Once we have wired it all up it will act the same as the standard HTML input from the previous example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation rules
&lt;/h2&gt;

&lt;p&gt;Now we want to add some validation rules to our field. We want the field to be required and we want it to be a valid email. The Controller component allows us to pass in rules object with our validation rules.&lt;/p&gt;

&lt;p&gt;In the code below, we are adding the rules object with the required rule and the pattern for the email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Controller&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"TextField"&lt;/span&gt;
  &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;required&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="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(([^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;()[&lt;/span&gt;&lt;span class="se"&gt;\]\\&lt;/span&gt;&lt;span class="sr"&gt;.,;:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;\"]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(\.[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;()[&lt;/span&gt;&lt;span class="se"&gt;\]\\&lt;/span&gt;&lt;span class="sr"&gt;.,;:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;\"]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;(\"&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;\"))&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;((\[[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\])&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;(([&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;\-&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.)&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}))&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Laravel there are many validation rules that we can choose from that allow us to validate our input, such as a built in rule for email. Here we have to define the pattern ourselves, which is fine, just a little less convenient.&lt;/p&gt;

&lt;p&gt;React Hook Form has less options, but they should cover most of your needs for client side validation. I would recommend doing server side validation on the server receiving the form data anyway.&lt;/p&gt;

&lt;p&gt;Now if we submit the form with no value, or without a valid email address it will no longer submit the form. The handleSubmit method checks the inputs against the rules and fails the validation.&lt;/p&gt;

&lt;p&gt;But right now we don’t have any feedback for the user to tell them the validation has failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation messages
&lt;/h2&gt;

&lt;p&gt;The useForm hook has a form state object, which contains an errors object. We can use this to display our errors to the user. We need to update our code so we can use this errors object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyFormInputs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can update our input component. The TextInput component has a boolean flag for isErrored, which when true makes the border a red colour. It also has an errorText method that allows us to pass in the error message.&lt;/p&gt;

&lt;p&gt;The isErrored can be checked to see if the errors object has the Email key. If it does, pass in true, which makes the border red.&lt;/p&gt;

&lt;p&gt;The errorText needs us to do a bit more work first. Laravel has a lot of predefined error messages that we can use, but with React Hook Form we need to define our own.&lt;/p&gt;

&lt;p&gt;This is done by updating the items in the rules object to be an object with value and message keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Controller&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"TextField"&lt;/span&gt;
  &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The email address is required.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(([^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;()[&lt;/span&gt;&lt;span class="se"&gt;\]\\&lt;/span&gt;&lt;span class="sr"&gt;.,;:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;\"]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(\.[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&amp;gt;()[&lt;/span&gt;&lt;span class="se"&gt;\]\\&lt;/span&gt;&lt;span class="sr"&gt;.,;:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;\"]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;(\"&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;\"))&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;((\[[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\.[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]{1,3}\])&lt;/span&gt;&lt;span class="sr"&gt;|&lt;/span&gt;&lt;span class="se"&gt;(([&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;\-&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.)&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}))&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The email address is invalid.&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="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
      &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
      &lt;span class="na"&gt;isErrored&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&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="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;errorText&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when we press submit, it validates the inputs as before, but now the TextInput will have a red border and the error message we defined will display alongside the input.&lt;/p&gt;

&lt;p&gt;If we don’t put any value in and press submit, it will display ‘The email address is required.’ and if we put in an invalid email address, such as with spaces, then it will display ‘The email address is invalid.’.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error summary
&lt;/h2&gt;

&lt;p&gt;Imagine we have a form with several inputs. When we press submit we may not see the validation error as it could be at the top of the form and out of the current view.&lt;/p&gt;

&lt;p&gt;We could display a summary of errors at the top of the form and scroll up to it. We need a way of knowing when the validation has failed. React Hook Form has this covered for us too.&lt;/p&gt;

&lt;p&gt;We can define a method for when the validation fails, which we will call onSubmitFailed. I’m not going to provide the code here, just giving you an idea of how it could work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSubmitFailed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Scroll to top of form&lt;/span&gt;
  &lt;span class="c1"&gt;// Display error summary&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then when we call handleSubmit, we can pass our new method in as the second argument. This callback will run when the validation fails and there are errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onSubmitFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We have gone from creating a simple form, to managing the state in our controlled components, to showing an error state and validation messages, to showing a summary of all the error messages.&lt;/p&gt;

&lt;p&gt;This library is very handy and has lots of really useful features. The documentation seems really great with examples and full API documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/city-skyline-68NE2VS3GG"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/mattbangophotos"&gt;Matt Bango&lt;/a&gt; on &lt;a href="https://stocksnap.io"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using when with the Laravel Http Client</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Sun, 28 Jan 2024 18:00:07 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/using-when-with-the-laravel-http-client-4a1l</link>
      <guid>https://dev.to/chrisrhymes/using-when-with-the-laravel-http-client-4a1l</guid>
      <description>&lt;p&gt;Here’s a little tip I discovered that I haven’t seen documented anywhere. You can use when() and unless() with the Laravel Http client.&lt;/p&gt;

&lt;p&gt;Here is an example method that uses the Laravel Http client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com/api'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&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 imagine that we wanted to pass a token in that is sent as a header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com/api'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&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 Http client makes this very easy by using the &lt;code&gt;-&amp;gt;withHeader()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;But what happens if the token is optional for some calls? Some requests need it and others don’t?&lt;/p&gt;

&lt;p&gt;Well, we could copy the whole method and duplicate all our code, or we could make use of &lt;code&gt;-&amp;gt;when()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you look into the PendingRequest class, you’ll see that it makes use of the &lt;code&gt;Illuminate\Support\Traits\Conditionable&lt;/code&gt; trait. This trait gives it access to both when() and unless().&lt;/p&gt;

&lt;p&gt;Here we set the token to be an optional parameter. When it is passed in, the when() resolves as true and then adds the closure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Client\PendingRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com/api'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;PendingRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&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;You can also set a default method if you need to which runs when the when() resolves to false. An example could be setting a default token in the header if one is not provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Client\PendingRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://example.com/api'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;PendingRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;PendingRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-Token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'default-value'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stocksnap.io/photo/city-skyline-0SE0ZRMZCM"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/focastock"&gt;FOCA Stock&lt;/a&gt; on &lt;a href="https://stocksnap.io"&gt;StockSnap&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>http</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
