<?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.us-east-2.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>Coding with Laravel Boost and Kiro AI</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/coding-with-laravel-boost-and-kiro-ai-16kp</link>
      <guid>https://dev.to/chrisrhymes/coding-with-laravel-boost-and-kiro-ai-16kp</guid>
      <description>&lt;p&gt;I recently built a Laravel app using Laravel Boost along with Kiro, Amazon’s AI service, and wanted to share what I discovered. This is more of an opinion piece as I try to put into words what I think about the new way of developing. This reflects my current thinking and I’m absolutely sure that each developer will have a different experience than mine.&lt;/p&gt;

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

&lt;p&gt;I started by creating a new Laravel project with the &lt;a href="https://laravel.com/framework/docs/13.x/installation#installing-php" rel="noopener noreferrer"&gt;Laravel installer&lt;/a&gt;. I selected the Livewire option as that was what I was already familiar with.&lt;/p&gt;

&lt;p&gt;I then installed &lt;a href="https://laravel.com/framework/docs/boost" rel="noopener noreferrer"&gt;Laravel Boost&lt;/a&gt; as a composer dev dependency and ran the install command, selecting Kiro as my AI tool. This created all of the files needed for the AI to know about Laravel and its recommended way of working.&lt;/p&gt;

&lt;p&gt;I then set up &lt;a href="https://kiro.dev/docs/cli/acp/#jetbrains-ides" rel="noopener noreferrer"&gt;Kiro in PHP Storm&lt;/a&gt; with the ACP so I could write the prompts directly in the IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first step - database
&lt;/h2&gt;

&lt;p&gt;I started with the same process I would with other apps I’ve built in the past, with the database models and migrations. I suppose I could have just written a prompt as to what I wanted to build and let it go and fill its boots, but I wanted to control the database structure. From past experience, getting the database structure and the relationships right early makes everything else easier down the line. I guess I just didn’t trust AI to do it how I wanted it.&lt;/p&gt;

&lt;p&gt;Laravel makes it pretty easy to get started with the scaffolding by allowing you to create a model, migration and factory with a single artisan command. I ran the commands, defined the tables in the migrations and then created the relationships in the models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the AI involved
&lt;/h2&gt;

&lt;p&gt;Now I had a foundation for the database schema in place, I asked the AI to create Livewire components and forms for each of the models. I did this for each model one by one so I could see what the output was and judge the changes it made. As part of the prompt I told it to use the database migrations to know what fields to create in each Livewire form, and I also asked it to use Livewire Flux UI (that was installed by default anyway) to build each form interface.&lt;/p&gt;

&lt;p&gt;The AI spat out a load of text into the chat window as it processed what I had asked it to do and started reading all the files in the project. I tried to keep up with the text, but it was outputting text too fast for me to read and often disappeared anyway after it had come to some kind of decision. I’m not really sure of the point of all that, I guess it’s instead of just showing a loading spinner? Maybe it looks more impressive to spam a load of text so fast you can’t read it. Anyway, I quickly gave up reading most of the text and waited for the text at the end that summarised what it did. The summary was still quite long, but it was broken into steps so you could skim read the bullet points if you wanted to.&lt;/p&gt;

&lt;p&gt;Interestingly, the AI asked for permission to do certain tasks, such as reading files and searching. After the first few times I just pressed ‘Yes’, but I always liked to take time and read what it was asking when it came to running scripts in the terminal. Just out of an abundance of caution.&lt;/p&gt;

&lt;p&gt;Long story short, (too late), it produced a clean looking interface using Flux UI and it mostly worked. It was surprising how little information I had to provide to make it build something. It seemed that having something for it to reference is key and having all the rules or guardrails in Laravel Boost helped steer the output to what I was familiar with.&lt;/p&gt;

&lt;p&gt;Once the AI had produced a component I could then provide another prompt to ask it to make a specific change to the component without giving it too much detail. It seemed to follow on from the previous prompt, using the existing context from the chat session. It allowed me to try things out and see the changes quickly without being too caught up in how long it would take to code it and take a prototyping kind of approach to get what I wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests and formatting
&lt;/h2&gt;

&lt;p&gt;One thing that I really liked about Laravel boost is that it instructs the AI to automatically write tests to confirm what it built works. I always write tests to ensure things work the way they should, but also to help document the functionality so future code changes would flag a change in behaviour. But writing tests can become laborious, so the AI writing them was definitely a bonus for me. It used PEST to write the tests which is also another win as they are written in a very fluent style so it was easy to read through the tests that had been written and quickly understand what they were doing.&lt;/p&gt;

&lt;p&gt;One thing I would say, if I were writing the tests myself I would have spent a bit longer writing more reusable tests and passing in datasets to test the different inputs and outputs. The AI could write tests so fast that I guess it’s easier for it to create simpler tests, but more of them. I’m wondering if that could lead to greater technical debt in future?&lt;/p&gt;

&lt;p&gt;It also ran Laravel Pint after each change, ensuring the formatting is what you would expect, keeping the code easy to read and consistent. I normally set up a pipeline to do this when I push code up to the remote repo, but having it format as you go is very useful.&lt;/p&gt;

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

&lt;p&gt;When trying out the user interface, I noticed a couple of things that weren’t quite right, so I jumped into the code myself and then made some changes. I then prompted the AI to add some extra functionality to the same Livewire component. It suddenly seemed confused that the files didn’t match what it was expecting. I can’t remember the exact wording, but it seemed very possessive over the files and like it wanted to discourage anyone else making changes to the stuff it had output.&lt;/p&gt;

&lt;p&gt;This was a bit of a shock to be honest. I wanted a tool that would work with me, not do everything for me. Once it had reindexed the updated files it got over itself and made the relevant changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Folder structure
&lt;/h2&gt;

&lt;p&gt;What I like about working with Laravel is the folder structure. It has a convention that has changed slightly over the years, but it is pretty consistent. For example, if you run an artisan command to create a notification, it will put it in a Notifications directory in the app directory.&lt;/p&gt;

&lt;p&gt;Laravel Boost helps the AI follow that convention, meaning if you are new to the project you can find what you are looking for quickly and start making changes. This is a massive efficiency gain that is often overlooked.&lt;/p&gt;

&lt;p&gt;I have to say, after looking at some other projects that are more towards being vibe coded, the consistency and structure is missing. If you open up the codebase you’ll have to spend a lot of time to figure out where everything is before you can start working on it.&lt;/p&gt;

&lt;p&gt;I sometimes wonder if this is deliberate, that AI puts things in different places in different projects so that you are more inclined to use AI to make changes in future, guaranteeing its future business model?&lt;/p&gt;

&lt;h2&gt;
  
  
  Comments, lots and lots of comments
&lt;/h2&gt;

&lt;p&gt;I like to write a few comments here and there in the code, but AI seems to love comments. It’s an LLM after all, so writing chunks of text is what it does best. But AI seems to write way too many comments to me.&lt;/p&gt;

&lt;p&gt;After considering this for a moment, I realised that AI has a finite memory. If you close a chat session and start a new one, it needs to relearn your codebase all over again. Laravel Boost helps it understand the structure, but the business logic is a mystery to it. Writing loads of comments is like its way of dumping its decision making into the code, so that next time it does something it can read through all the comments and remember why something was done that specific way.&lt;/p&gt;

&lt;p&gt;Developers definitely work like that too with comments by adding notes for themselves and others in future, but once you become familiar with a project you remember why things are done a certain way. You’ve been in the meetings with the customer when the business decisions are made, but AI doesn’t have that ability. Well, maybe that’s the next evolution of what’s to come…&lt;/p&gt;

&lt;p&gt;Part of being a good developer is writing code that is readable. To me, this helps reduce the need for so many comments. This is where real world developer experience can count.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what did I think overall?
&lt;/h2&gt;

&lt;p&gt;It was certainly quick to create new components and tests. It seemed to produce consistent and readable code, but I think that is more due to the Laravel way of doing things and the AI following the standards set out in the Boost package.&lt;/p&gt;

&lt;p&gt;But was it fun?&lt;/p&gt;

&lt;p&gt;I have to say no. It definitely made things easier, but the difficult bits are what make a job worth doing. There is a lot of job satisfaction to be had in working through a problem and finding the best solution. That part of the job is now reduced with AI.&lt;/p&gt;

&lt;p&gt;The following quote seemed to resonate with me when thinking about how AI coding works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It’s not the destination, it’s the journey”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The journey is what makes great software and AI seems to jump straight to the destination.&lt;/p&gt;

&lt;p&gt;I tend to first build something that works, but then spend more time afterwards enhancing and optimising it. This part of the process, this part of the struggle, actually provides inspiration for how something could be further improved. It gives your mind time to think about the possibilities, making those minute decisions as you go along that end up with a great result.&lt;/p&gt;

&lt;p&gt;Maybe the developer being the bottleneck was a good thing after all?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/boston-airport-KVDROEGWGZ" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/mattmoloney" rel="noopener noreferrer"&gt;Matt Moloney&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>laravel</category>
      <category>ai</category>
    </item>
    <item>
      <title>Updating my website's theme</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 15 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/updating-my-websites-theme-1j9j</link>
      <guid>https://dev.to/chrisrhymes/updating-my-websites-theme-1j9j</guid>
      <description>&lt;p&gt;As a web developer, your own website can act as a portfolio for your work, showcasing all of your other work and projects, but sometimes it is the last thing that gets the attention it deserves. I decided that my website needed a facelift, and I decided to move away from my old theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulma-Clean-Theme
&lt;/h2&gt;

&lt;p&gt;I first built &lt;a href="https://github.com/chrisrhymes/bulma-clean-theme" rel="noopener noreferrer"&gt;bulma-clean-theme&lt;/a&gt; to power my own website in September 2018. I would say that being able to use the same theme for over eight years is a sign of success. The theme has been downloaded over 130 thousand times from the Ruby Gems directory and I have released many versions with tweaks and changes, along with many contributions from other developers that have helped make it such a success.&lt;/p&gt;

&lt;p&gt;But…&lt;/p&gt;

&lt;p&gt;The Bulma CSS framework hasn’t had a new version released in over a year now. It works well, but it feels like it is time for me to look at alternatives and something that will offer me more flexibility to do my own thing.&lt;/p&gt;

&lt;p&gt;Bulma-clean-theme is still available to use, but I wanted to make my own website how I want it to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  TailwindCSS
&lt;/h2&gt;

&lt;p&gt;One of the other themes I had been working on occasionally was &lt;a href="https://purpletual.netlify.app/" rel="noopener noreferrer"&gt;Purpletual blog theme&lt;/a&gt; that used TailwindCSS. This is a simple blog theme (with lots of purple) but uses PostCSS to compile the TailwindCSS code. It was a starting point for me to learn how to use TailwindCSS.&lt;/p&gt;

&lt;p&gt;I have since used TailwindCSS in other work projects and it took me a little while to get to grips with it, but once you are familiar with the utility classes it feels like second nature. I decided that this was what I wanted to use on my own website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;I’ll be honest, I’m not really a web designer. I would class myself as a developer first and foremost. But what I like about TailwindCSS is that it’s easy to try things out, easy to prototype something and see it come together bit by bit.&lt;/p&gt;

&lt;p&gt;I started looking at the &lt;a href="https://tailwindcss.com/showcase" rel="noopener noreferrer"&gt;TailwindCSS showcase&lt;/a&gt; page and found some aspects of designs that I liked. What’s great with TailwindCSS is that you can inspect the page that others have built and see the styling building blocks by simply reading the utility classes. I found this really inspirational and gave me some ideas for my own website design.&lt;/p&gt;

&lt;p&gt;One nice little feature was the background image fixed (the &lt;code&gt;bg-fixed&lt;/code&gt; class), which I used on medium and up screen sizes on the hero image. This is a little nod back to late 2000’s when parallax scrolling was all the rage. Back then we used a variety of jQuery libraries to achieve the effect, but now it’s all in one CSS class. Amazing how things have changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final design?
&lt;/h2&gt;

&lt;p&gt;So is this the final design? I would say no, at least for now. Doing this upgrade has allowed me to build a new design, but it also allows me to experiment with the design going forward and enhance it more. The old theme did a great job, but now I can spend some time building something more unique and improving my web design skills over time.&lt;/p&gt;

&lt;p&gt;One last thing…&lt;/p&gt;

&lt;p&gt;I just wanted to say thank you to bulma-clean-theme and the 8 years it has served me powering my website, but it seems nothing stays the same in web development forever.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>jekyll</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>First impressions of Flux UI</title>
      <dc:creator>C.S. Rhymes</dc:creator>
      <pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/chrisrhymes/first-impressions-of-flux-ui-2gn6</link>
      <guid>https://dev.to/chrisrhymes/first-impressions-of-flux-ui-2gn6</guid>
      <description>&lt;p&gt;Recently I had the opportunity to work on a new side project. I had already decided I wanted to use Laravel and Livewire, but I wasn’t sure about the frontend user interface. I had seen some demos of Flux UI and liked what I had seen, so decided to give it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;First things first, the installation was very easy in a new Laravel app as &lt;a href="https://fluxui.dev/" rel="noopener noreferrer"&gt;Flux UI&lt;/a&gt; was installed by default when I selected Livewire as the starter kit using the Laravel installer. This meant I could get started developing straight away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming
&lt;/h2&gt;

&lt;p&gt;Initially I wasn’t concerned about the styling and kept things standard, but later on I decided to change things up and add some colour. Luckily the Flux UI website has a &lt;a href="https://fluxui.dev/themes" rel="noopener noreferrer"&gt;tool for building a theme&lt;/a&gt; and you just need to copy and paste the output into the app.css file, then the changes were shown straight away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local development
&lt;/h2&gt;

&lt;p&gt;This is more of a Laravel feature than Flux, but it was the first time I have used the &lt;code&gt;composer run dev&lt;/code&gt; command to run the development environment. This made it really easy to get started developing with a SQLite database and the local installation of PHP. No need for docker or Laravel Sail. This was really handy for me as I was developing on a MacBook Air, rather than my more powerful work machine, and meant I didn’t need to do much set up to get working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth views and dashboard
&lt;/h2&gt;

&lt;p&gt;So everything was installed and up and running, so I started to build my new Laravel app. The first thing I really appreciated was that the scaffolding and views were all in place for registering and logging in. Great.&lt;/p&gt;

&lt;p&gt;Next thing, once I had registered a user I was shown a dashboard starter screen. This had some great features built in, allowing users to manage their profile and a nice sidebar layout where I could add links too. Another worry off my mind!&lt;/p&gt;

&lt;h2&gt;
  
  
  Header and Sidebar
&lt;/h2&gt;

&lt;p&gt;Here things slowed down a little at first as I got used to the way Flux UI works. The header and sidebar is built up of a lot of smaller components. This makes them really reusable, but there is quite a lot to take in. I think the Header and Sidebar pages could do with a little more detail in the documentation, helping to explain how on desktop it shows links in the header, then on mobile and tablet it uses the sidebar. Once you get your head around this it’s very easy to use. Other frameworks I’ve used tend to have one menu which has two different sets of styles for mobile devices and desktop screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms and inputs
&lt;/h2&gt;

&lt;p&gt;Building a form was very easy using the Livewire methods I was used to and hooking them up to the relevant flux inputs was very simple too, using the flux:input shorthand syntax and passing in the wire:model and the label. What’s really nice is the inputs also handle displaying the validation error messages for you without any extra work.&lt;/p&gt;

&lt;p&gt;The app I was building had a couple of file uploads. I saw that there was a File Upload component, but it wasn’t included in the standard Flux UI package. What was great though, was that the standard flux:input allowed you to set the type to file and use this for file uploads. Although it doesn’t have all the features of the pro version of Flux UI, it did allow me to keep building what I needed for now and fits in nicely with the rest of the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tables
&lt;/h2&gt;

&lt;p&gt;I also tried out the table component for an index page with a data table. This has a lot of really nice features built in, such as sorting/ordering columns, pagination and sticky headers. The way the component names are namespaced is very logical to me too, so you start off with a flux:table, then flux:table.columns, which have multiple flux:table.column inside.&lt;/p&gt;

&lt;p&gt;What I really liked about Flux UI was the fact that it seems to have most standard use cases catered for. You can start with an empty app and quickly and easily build up a user interface that looks neat and tidy and functions well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;One thing that I am unsure about is the accessibility. Everything seems very small to me, but maybe that’s from my day job working in local government where we try and meet WCAG AAA where possible, making interactive controls at least 44px wide and 44px high. Reflecting on this though, Flux UI has similar look and feel to GitHub for navigation and sidebars, which I use daily without issue. A standard flux:input is around 40px high so it’s not far off the AAA standard, but sidebar links are closer to 30px high so a bit further away from that standard.&lt;/p&gt;

&lt;p&gt;I’m pretty sure I could spend some time looking at how to overwrite the default styles in the theme to achieve this if I needed too. Flux UI uses TailwindCSS and again, works really well with it, allowing you to customise the theme and ensure sufficient colour contrast, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I use Flux UI again?
&lt;/h2&gt;

&lt;p&gt;Yes, definitely. You can tell it’s built by the same people that built Livewire. It works so well together and makes building a Laravel app very easy. I like to work on the functionality and features of an app and know that the UI will just work without much hassle. Like I said earlier, there is a bit of learning to do, but once you get your head around it is very logical to use. There also seems to be regular updates and new components and features added regularly which is a great sign for the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stocksnap.io/photo/citylights-buildings-YP7B573GOA" rel="noopener noreferrer"&gt;Photo&lt;/a&gt; by &lt;a href="https://stocksnap.io/author/burstshopify" rel="noopener noreferrer"&gt;Burst&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>laravel</category>
      <category>livewire</category>
    </item>
    <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>
  </channel>
</rss>
