<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>C.S. Rhymes</title>
    <description>C.S. Rhymes author of How NOT to make a website and How NOT to use a smartphone
</description>
    <link>https://www.csrhymes.com/</link>
    <atom:link href="https://www.csrhymes.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Tue, 24 Feb 2026 15:21:45 +0000</pubDate>
    <lastBuildDate>Tue, 24 Feb 2026 15:21:45 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Hosting a Next.js site with AWS Elastic Beanstalk</title>
        <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 id=&quot;getting-started&quot;&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;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx create-next-app@latest my-app &lt;span class=&quot;nt&quot;&gt;--yes&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;my-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;hooks&quot;&gt;Hooks&lt;/h2&gt;
&lt;p&gt;When you set the platform to node.js, Elastic Beanstalk should automatically run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt; to install your dependencies, but it doesn’t automatically run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;hooks&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;confighooks&lt;/code&gt; directories, then create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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;ul&gt;
          &lt;li&gt;predeploy/&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;confighooks/
        &lt;ul&gt;
          &lt;li&gt;predeploy/&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /var/app/staging
npm &lt;span class=&quot;nb&quot;&gt;install
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/app/current&lt;/code&gt; directory,&lt;/li&gt;
  &lt;li&gt;It then renames the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/app/staging&lt;/code&gt; directory to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/platforms-linux-extend.hooks.html&quot;&gt;platform hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;procfile&quot;&gt;Procfile&lt;/h2&gt;

&lt;p&gt;Beanstalk has a default run command of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;web: node_modules/.bin/next start -p $PORT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;h2 id=&quot;setting-the-server-type&quot;&gt;Setting the server type&lt;/h2&gt;

&lt;p&gt;By default, Beanstalk uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ebextensions&lt;/code&gt; directory in your project root, then create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;option_settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Min 4GB RAM per instance for Next&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The next build times out with micro servers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;aws:ec2:instances&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;InstanceTypes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;t4g.medium, t4g.large&lt;/span&gt;
    
  &lt;span class=&quot;c1&quot;&gt;# Configure the port the proxy server (nginx) listens on&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/nodejs-platform-proxy.html&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;aws:elasticbeanstalk:application:environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3000&lt;/span&gt;
    
  &lt;span class=&quot;c1&quot;&gt;# Launch a single EC2 instance without a load balancer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;aws:elasticbeanstalk:environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;EnvironmentType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;SingleInstance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-a-beanstalk-application&quot;&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=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;AWS CLI&lt;/a&gt; and the &lt;a href=&quot;https://github.com/aws/aws-elastic-beanstalk-cli&quot;&gt;Elastic Beanstalk CLI&lt;/a&gt;, as well as &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;example-next&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb init example-next &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; my-aws-profile 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;creating-a-beanstalk-environment&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt; we can remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_modules&lt;/code&gt; directory from our project before running the create or deploy commands to make the upload smaller.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb create staging &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; my-aws-profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;staging&lt;/code&gt; directory, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 id=&quot;opening-the-environment&quot;&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;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb open staging &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; my-aws-profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;deploying-updates&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;eb deploy&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eb create&lt;/code&gt; as the environment already exists. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;staging&lt;/code&gt; in the below command is the name of the environment you want to update.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb deploy staging &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; my-aws-profile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;adding-environment-variables&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://stocksnap.io/photo/developer-code-NT1Q3GZVFI&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/morillo&quot;&gt;Christina Morillo&lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Feb 2026 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2026/02/05/hosting-a-next-js-site-with-aws-elastic-beanstalk.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2026/02/05/hosting-a-next-js-site-with-aws-elastic-beanstalk.html</guid>
        
        <category>webdev</category>
        
        <category>hosting</category>
        
        <category>javascript</category>
        
        
      </item>
    
      <item>
        <title>Creating a links page with Bulma Clean Theme</title>
        <description>&lt;p&gt;I’ve seen a few posts recently asking what other authors use for their website. There are many options available, but sometimes you just want a single page with links to your social media profiles and links to your books. This is where Bulma Clean Theme and GitHub pages can come to the rescue.&lt;/p&gt;

&lt;p&gt;The below may seem a bit of a different workflow to begin with from other website builders, as it is defining content and configuration in files instead of through a user interface. It will get easier and more familiar the more you use it.&lt;/p&gt;

&lt;p&gt;Here is an &lt;a href=&quot;https://www.csrhymes.com/LinksPage/&quot;&gt;example Links Page&lt;/a&gt;, showing what we will achieve by the end of this article.&lt;/p&gt;

&lt;h2 id=&quot;forking-the-linkspage-repo&quot;&gt;Forking the LinksPage repo&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Create a GitHub account (if you don’t already have one)&lt;/li&gt;
  &lt;li&gt;Head over to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://github.com/chrisrhymes/LinksPage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Press the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fork&lt;/code&gt; button at the top right&lt;/li&gt;
  &lt;li&gt;Select your user as the owner&lt;/li&gt;
  &lt;li&gt;Your repository must be named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;user&amp;gt;.github.io&lt;/code&gt;, replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;user&amp;gt;&lt;/code&gt; with your username.&lt;/li&gt;
  &lt;li&gt;Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create fork&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;editing-your-site&quot;&gt;Editing your site&lt;/h2&gt;

&lt;p&gt;So now you have a bunch of files in your repo, but these have sample content in so we need to modify the files to have your content.&lt;/p&gt;

&lt;p&gt;We can open up a code editor in &lt;a href=&quot;https://docs.github.com/en/codespaces/developing-in-a-codespace/developing-in-a-codespace&quot;&gt;GitHub Codespaces&lt;/a&gt;. On your repo’s code tab, click the green Code button, then select the Codespaces tab, then click the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; to create a new codespace. If you already have a codespace running then select it from the list.&lt;/p&gt;

&lt;h3 id=&quot;create-an-images-folder&quot;&gt;Create an images folder&lt;/h3&gt;

&lt;p&gt;Create a folder for your images in the root of your repo called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;images&lt;/code&gt;. You can upload the images for your site into this folder and reference them from the other pages using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/images/image-file-name.jpg&lt;/code&gt; style of paths.&lt;/p&gt;

&lt;h3 id=&quot;update-the-_configyml-file&quot;&gt;Update the _config.yml file&lt;/h3&gt;

&lt;p&gt;The _config.yml file contains the sitewide settings for your site.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Update the title to your author name or pen name&lt;/li&gt;
  &lt;li&gt;Update the description to describe your books&lt;/li&gt;
  &lt;li&gt;Update the baseurl to be empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&quot;&quot;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Update the list of social networks to point to your profile pages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;update-the-indexmd-file&quot;&gt;Update the index.md file&lt;/h3&gt;

&lt;p&gt;This is the file for your homepage. It has a section at the top which is called front matter, which is the configuration for the page. The front matter is contained within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;---&lt;/code&gt; at the top and the bottom. Below the second &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;---&lt;/code&gt; is the page content and can be written in markdown format.&lt;/p&gt;

&lt;p&gt;This page uses the links layout from Bulma Clean Theme. The front matter is written in yaml format.&lt;/p&gt;

&lt;p&gt;Have a read of the &lt;a href=&quot;https://www.csrhymes.com/bulma-clean-theme/docs/links-pages/creating-a-links-page/&quot;&gt;Links page documentation&lt;/a&gt; on the Bulma Clean Theme website for full instructions, but essentially the links are broken into sections so each section of links can have a title and then a list of links below.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;link_sections&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;The section title&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Read the docs&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/docs/&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://picsum.photos/id/69/300/300&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;image_alt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;An example image&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Read the Blog&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/blog/&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://picsum.photos/id/70/300/300&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;image_alt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;An example image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;commiting-the-changes&quot;&gt;Commiting the changes&lt;/h3&gt;

&lt;p&gt;Once you are happy with your changes to your files, you need to add and then commit the changes to the GitHub repo.&lt;/p&gt;

&lt;p&gt;Follow the instructions for &lt;a href=&quot;https://docs.github.com/en/codespaces/developing-in-a-codespace/using-source-control-in-your-codespace#committing-your-changes&quot;&gt;how to commit changes in a GitHub codespace&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;setting-up-github-pages-hosting&quot;&gt;Setting up GitHub pages hosting&lt;/h2&gt;

&lt;p&gt;Once you have made your content changes, you will want to set up the hosting for your page using GitHub pages. We are using GitHub actions to build and deploy the site. This means it will automatically trigger a rebuild each time you commit the changes to your main branch and then deploy the changes.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Go to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings&lt;/code&gt; tab from within your freshly forked repositiory&lt;/li&gt;
  &lt;li&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pages&lt;/code&gt; from the left side menu&lt;/li&gt;
  &lt;li&gt;Under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build and deployment&lt;/code&gt; select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GitHub Actions&lt;/code&gt; from the drop down&lt;/li&gt;
  &lt;li&gt;It should build your site using the built in GitHub Actions workflow&lt;/li&gt;
  &lt;li&gt;When it has finished, it should have a link at the top of the Pages settings page&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;adding-a-custom-domain&quot;&gt;Adding a custom domain&lt;/h2&gt;

&lt;p&gt;The site will be available on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;user&amp;gt;.github.io&lt;/code&gt; but you probably want to configure a custom domain name.&lt;/p&gt;

&lt;p&gt;This will vary based on your domain and the provider. Take a look through the GitHub documentation for &lt;a href=&quot;https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/about-custom-domains-and-github-pages&quot;&gt;how to set up your custom domain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should now have your links page up and running and have somewhere you can add links and share on your social media profile pages.&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/11/23/creating-a-links-page-with-bulma-clean-theme.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/11/23/creating-a-links-page-with-bulma-clean-theme.html</guid>
        
        <category>webdev</category>
        
        <category>html</category>
        
        <category>links</category>
        
        
      </item>
    
      <item>
        <title>HTML is beautiful</title>
        <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 id=&quot;html-is-simple&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tag with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements&quot;&gt;HTML elements reference&lt;/a&gt; for a full breakdown.&lt;/p&gt;

&lt;h2 id=&quot;html-is-future-proof&quot;&gt;HTML is future proof&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://info.cern.ch/&quot;&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 id=&quot;html-is-graceful&quot;&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=&quot;https://www.pietschsoft.com/post/2010/11/14/html5-day-1-new-tags-work-in-older-browsers-awesome&quot;&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=&quot;https://en.wikipedia.org/wiki/HTML5&quot;&gt;https://en.wikipedia.org/wiki/HTML5&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;html-is-responsive&quot;&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 id=&quot;html-is-beautiful&quot;&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  // More form content
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submit&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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>
        <pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/10/07/html-is-beautiful.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/10/07/html-is-beautiful.html</guid>
        
        <category>webdev</category>
        
        <category>html</category>
        
        
      </item>
    
      <item>
        <title>Fixing a few SEO issues with my author website</title>
        <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 id=&quot;www-or-non-www-subdomain&quot;&gt;www or non-www subdomain&lt;/h2&gt;

&lt;p&gt;The site is built using Jekyll, which has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; file where you can specify your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://www.littleastwick.co.uk&quot;&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 id=&quot;what-was-the-impact&quot;&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 www.littleastwick.co.uk 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 id=&quot;how-was-this-fixed&quot;&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;img src=&quot;/img/netlify-domains.jpg&quot; alt=&quot;Netlify domain configuration&quot; /&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 id=&quot;pitfalls-avoided&quot;&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 id=&quot;permalinks&quot;&gt;Permalinks&lt;/h2&gt;

&lt;p&gt;Jekyll has the option for you to set the &lt;a href=&quot;https://jekyllrb.com/docs/permalinks/&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 id=&quot;what-was-the-impact-1&quot;&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 id=&quot;how-was-this-fixed-1&quot;&gt;How was this fixed?&lt;/h3&gt;

&lt;p&gt;My resolution to this was to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;permalinks: pretty&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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 id=&quot;potential-pitfalls-avoided&quot;&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 id=&quot;404-page&quot;&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 id=&quot;what-was-the-impact-2&quot;&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 id=&quot;how-was-this-fixed-2&quot;&gt;How was this fixed?&lt;/h3&gt;

&lt;p&gt;This was resolved by creating a new custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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=&quot;https://jekyllrb.com/tutorials/custom-404-page/&quot;&gt;custom 404 documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&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=&quot;https://search.google.com/search-console/about&quot;&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=&quot;https://github.com/jekyll/jekyll-redirect-from&quot;&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>
        <pubDate>Sat, 23 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/08/23/fixing-a-few-seo-issues-with-my-author-website.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/08/23/fixing-a-few-seo-issues-with-my-author-website.html</guid>
        
        <category>webdev</category>
        
        <category>author</category>
        
        <category>seo</category>
        
        
      </item>
    
      <item>
        <title>Using Tailwindcss with Codepen</title>
        <description>&lt;p&gt;I created a free account for Codepen to provide a demo with my blog post about ‘Creating a custom toggle in TailwindCSS’ but it took me a little while to figure out how to use Tailwindcss with codepen. So, this is what I did to get it working.&lt;/p&gt;

&lt;h2 id=&quot;add-the-play-cdn&quot;&gt;Add the Play CDN&lt;/h2&gt;

&lt;p&gt;Looking at the &lt;a href=&quot;https://tailwindcss.com/docs/installation/play-cdn&quot;&gt;get started guide&lt;/a&gt; in Tailwindcss documentation, it states:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use the Play CDN to try Tailwind right in the browser without any build step. The Play CDN is designed for development purposes only, and is not intended for production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width, initial-scale=1.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First I copied and pasted the section from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tags in the Tailwind into the HTML section of Codepen, &lt;strong&gt;but you shouldn’t do this&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I got a warning in Codepen saying the HTML section is only for content inside the body tags. So this needed to go somewhere else.&lt;/p&gt;

&lt;h2 id=&quot;pen-settings&quot;&gt;Pen settings&lt;/h2&gt;

&lt;p&gt;Instead of putting the above in the HTML section, you need to open the Pen settings, by clicking on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings&lt;/code&gt; button with the big cog.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/pen-settings.jpg&quot; alt=&quot;Codepen settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once in the settings, click on the HTML section on the left, then find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stuff for &amp;lt;head&amp;gt;&lt;/code&gt; section and paste it in there.&lt;/p&gt;

&lt;p&gt;Then click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Save and close&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;away-you-go&quot;&gt;Away you go&lt;/h2&gt;

&lt;p&gt;Now you can start writing your HTML in the HTML section and adding the Tailwindcss utility classes. The page will refresh and display your styled content.&lt;/p&gt;

&lt;h2 id=&quot;customising-the-theme&quot;&gt;Customising the theme&lt;/h2&gt;

&lt;p&gt;As the Tailwindcss documentation states, you can also customise your theme by adding a style tag with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type=&quot;text/tailwindcss&quot;&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;This seems to work in either the main HTML section or within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stuff for &amp;lt;head&amp;gt;&lt;/code&gt; section in the settings.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/tailwindcss&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;@theme&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--color-clifford&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;#da373d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-clifford&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Test&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://stocksnap.io/photo/bridge-highway-09NVK7X9GG&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/sergei&quot;&gt;Sergei Gussev&lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 29 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/07/29/using-tailwindcss-with-codepen.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/07/29/using-tailwindcss-with-codepen.html</guid>
        
        <category>webdev</category>
        
        <category>css</category>
        
        <category>tailwind</category>
        
        
      </item>
    
      <item>
        <title>Creating a custom toggle in TailwindCSS</title>
        <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 id=&quot;starting-with-the-checkbox&quot;&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;Off label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;On label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;basic-styles&quot;&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=&quot;https://tailwindcss.com/docs/display#screen-reader-only&quot;&gt;sr-only&lt;/a&gt; utility for this.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inline-flex&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cursor-pointer flex flex-row rounded-full bg-violet-700 px-2 py-2&quot;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sr-only&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mr-2 inline-flex cursor-pointer rounded-full bg-white px-4 py-2 text-black&quot;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Off label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inline-flex cursor-pointer rounded-full bg-violet-700 px-4 py-2 text-white&quot;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;On label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-peer&quot;&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=&quot;https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-sibling-state&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;peer&lt;/code&gt; to the checkbox class:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;peer sr-only&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then we can add the styles for the checked state for the off label using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;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&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  Off label
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;inline-flex cursor-pointer rounded-full bg-violet-700 px-4 py-2 text-white peer-checked:bg-white peer-checked:text-black&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  On label
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;adding-an-outline-with-focus-within&quot;&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=&quot;https://tailwindcss.com/docs/hover-focus-and-other-states#focus-within&quot;&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 class=&quot;language-plaintext highlighter-rouge&quot;&gt;focus-within:&lt;/code&gt; on the outer label.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cursor-pointer flex flex-row rounded-full bg-violet-700 px-2 py-2 focus-within:outline-4 focus-within:outline-amber-400&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Input content here --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;adding-a-transition&quot;&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=&quot;https://tailwindcss.com/docs/transition-property&quot;&gt;transition&lt;/a&gt; utility and add it to both spans.&lt;/p&gt;

&lt;p&gt;Here we add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&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;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;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&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  Off label
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;final-example&quot;&gt;Final example&lt;/h2&gt;

&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;KwdNZMy&quot; data-pen-title=&quot;Untitled&quot; data-user=&quot;chrisrhymes&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/chrisrhymes/pen/KwdNZMy&quot;&gt;
  Untitled&lt;/a&gt; by CS Rhymes (&lt;a href=&quot;https://codepen.io/chrisrhymes&quot;&gt;@chrisrhymes&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://public.codepenassets.com/embed/index.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;further-improvements&quot;&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=&quot;https://stocksnap.io/photo/night-cityscape-7AVEXUYJHQ&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/candacemcdaniel&quot;&gt;Candace McDaniel&lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 29 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/07/29/creating-a-custom-toggle-in-tailwindcss.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/07/29/creating-a-custom-toggle-in-tailwindcss.html</guid>
        
        <category>webdev</category>
        
        <category>css</category>
        
        <category>tailwind</category>
        
        
      </item>
    
      <item>
        <title>Five book blurb writing observations</title>
        <description>&lt;p&gt;Amazon KDP gives you a basic text editor for your book’s blurb, but here are five observations that I have made from researching other books. All of the examples are taken from Mystery books in the Amazon UK store.&lt;/p&gt;

&lt;h2 id=&quot;1-use-bold-text&quot;&gt;1. Use bold text&lt;/h2&gt;

&lt;p&gt;Use bold text in your description to highlight important hooks or tropes to catch the readers attention. Highlighting words or lines helps them stand out from paragraphs of blurb text.&lt;/p&gt;

&lt;p&gt;You want to ensure that there is a good mix of plain text and bold text. Use too much bold and nothing stands out!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blurb/bold-text.jpg&quot; alt=&quot;An example of bold text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/None-This-True-lies-could-ebook/dp/B0BH7VSYVZ&quot;&gt;None of this is true by Lisa Jewell&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;2-keep-those-paragraphs-short&quot;&gt;2. Keep those paragraphs short&lt;/h2&gt;

&lt;p&gt;Break up your blurb into shorter paragraphs to make them easier to read and digest.&lt;/p&gt;

&lt;p&gt;See what I did there…&lt;/p&gt;

&lt;p&gt;Just like this example, which also uses short, punchy sentences too.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blurb/short-paragraphs.jpg&quot; alt=&quot;An example of short paragraphs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/Waiting-Pre-order-Brand-Ballard-Thriller-ebook/dp/B0CTC685TB&quot;&gt;The Waiting by Michael Connelly&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;3-use-stars-alongside-text&quot;&gt;3. Use stars alongside text&lt;/h2&gt;

&lt;p&gt;Use stars &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; alongside bold text to help separate sections of your blurb. You don’t have the ability to add heading tags higher than h4 in the KDP editor, so you can be a bit creative with the other tools you do have access to.&lt;/p&gt;

&lt;p&gt;For example, you could do something like the following to separate the blurb from some reviews.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;***Readers loved this book!***&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blurb/stars.jpg&quot; alt=&quot;An example of using stars to separate text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/Should-Have-Been-unputdownable-psychological-ebook/dp/B0D8L5W52Y&quot;&gt;It should have been you by Andrea Mara&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;4-highlight-character-names&quot;&gt;4. Highlight character names&lt;/h2&gt;

&lt;p&gt;Highlight main character names in bold when introducing them. To me, this will grab my attention if I’ve read previous books in the series and already know the characters, but it will also work great for new series, helping to introduce the new characters!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blurb/character-names.jpg&quot; alt=&quot;An example of highlighting character names&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/We-Solve-Murders-bestselling-Thursday/dp/0241997488&quot;&gt;We solve murders by Richard Osman&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;5-end-on-a-question&quot;&gt;5. End on a question&lt;/h2&gt;

&lt;p&gt;Ending on a question leaves the reader only one choice. If they want to know the answer then they need to buy the book!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/blurb/question.jpg&quot; alt=&quot;An example of ending on a question&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.amazon.co.uk/None-This-True-lies-could-ebook/dp/B0BH7VSYVZ&quot;&gt;None of this is true by Lisa Jewell&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;trying-it-out&quot;&gt;Trying it out&lt;/h2&gt;

&lt;p&gt;I have been through the blurbs for my cozy mystery series, &lt;a href=&quot;https://www.amazon.co.uk/dp/B0D957HW6W&quot;&gt;The Little-Astwick Mysteries&lt;/a&gt;, and rewritten them to try out these techniques. I plan to write a follow up blog post in the future to let you know if there was any immediate impact.&lt;/p&gt;

&lt;p&gt;If you have any blurb writing tactics that worked for you, please share them in the comments!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://stocksnap.io/photo/books-library-GTWPBBTRRN&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/suzyhazelwood&quot;&gt;Suzy Hazelwood&lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 06 Jun 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/06/06/blurb-writing-observations.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/06/06/blurb-writing-observations.html</guid>
        
        <category>author</category>
        
        <category>writing</category>
        
        <category>books</category>
        
        
      </item>
    
      <item>
        <title>Creating a Kindle formatted book from Google Docs</title>
        <description>&lt;p&gt;I have seen a few posts on Threads recently asking what software people use to format their books. This is one option out of many, but I thought I would share my current workflow to give authors an insight into the pros and cons.&lt;/p&gt;

&lt;p&gt;I use Google Docs to write. This is because I find it easy to use, it’s free, has a grammar and spell checker, and it syncs to the cloud and between my various devices. I have been known to do some writing on my mobile phone whilst sat in a coffee shop and the Google Docs app allows me to easily pick up where I have left off on another device.&lt;/p&gt;

&lt;p&gt;I then use Kindle Create to format the book ready for uploading to Kindle Direct Publishing.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/books&quot;&gt;Find out more about my books&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;writing-in-google-docs&quot;&gt;Writing in Google Docs.&lt;/h2&gt;

&lt;p&gt;You can just get stuck in and start writing in Google Docs, but there are a couple of things that I’d like to mention that make life easier later on.&lt;/p&gt;

&lt;h3 id=&quot;headings&quot;&gt;Headings&lt;/h3&gt;

&lt;p&gt;Rather than making your chapter titles simple bold text, try and use headings. For chapters, I use ‘Heading 1’. This offers three advantages.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;It looks nice. Okay, that’s not really that important.&lt;/li&gt;
  &lt;li&gt;It gives you an easy way of jumping between chapters using the links in the Document Tabs. I have found this a life saver when reading back through your manuscript and even more so when it comes to editing.&lt;/li&gt;
  &lt;li&gt;When you import it into Kindle Create, it can partially detect the headings and automatically insert the chapters for you saving you time and your sanity. For this to work, you also need to insert a ‘Page break’ between each chapter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;indentation&quot;&gt;Indentation&lt;/h3&gt;

&lt;p&gt;I have had the tendency to insert an empty line between paragraphs of text. This is probably due to the way I write content form my blog in markdown. This is probably fine for non-fiction books, but fiction books tend to have each paragraph indented, making it easier to read.&lt;/p&gt;

&lt;p&gt;I recently updated one of my books in Kindle Create and manually indented each paragraph. All I can say is, I’m glad it was a novella. I still need to go through others and do the same reformatting when I have some time.&lt;/p&gt;

&lt;p&gt;You get the gist of what I am saying from this TikTok…&lt;/p&gt;

&lt;figure class=&quot;image is-16by9&quot;&gt;
    &lt;iframe src=&quot;https://www.tiktok.com/player/v1/7499755049933901078?controls=1&amp;amp;rel=0&amp;amp;autoplay=0&quot; class=&quot;has-ratio&quot;&gt;&lt;/iframe&gt;
&lt;/figure&gt;

&lt;p&gt;To save you having to do this, you can update the settings so Google Docs automatically indents the paragraphs for you.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Indent a paragraph of text.&lt;/li&gt;
  &lt;li&gt;Click on the Styles dropdown and then hover over Normal text,&lt;/li&gt;
  &lt;li&gt;Hover over the arrow on the right&lt;/li&gt;
  &lt;li&gt;Click on “Update ‘Normal text’ to match”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This should mean that each new paragraph going forward will be indented automatically.&lt;/p&gt;

&lt;h3 id=&quot;line-spacing&quot;&gt;Line spacing&lt;/h3&gt;

&lt;p&gt;I don’t think this makes a difference to Kindle Create, but I like to up the line spacing from the default 1.15 to 1.5. This makes it easier for me to read (and looks like I have written more words than I really have).&lt;/p&gt;

&lt;h2 id=&quot;exporting-from-google-docs&quot;&gt;Exporting from Google Docs&lt;/h2&gt;

&lt;p&gt;Once you have your finished manuscript, you are ready to export. Kindle Create only accepts .doc, .docx or .rtf formats, so we need to export to one of the compatible formats.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Click on ‘File’&lt;/li&gt;
  &lt;li&gt;Hover over ‘Download’&lt;/li&gt;
  &lt;li&gt;Click on ‘Microsoft Word (.docx)’&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your manuscript will be downloaded into a Word formatted file.&lt;/p&gt;

&lt;h2 id=&quot;importing-into-kindle-create&quot;&gt;Importing into Kindle Create&lt;/h2&gt;

&lt;p&gt;Kindle Create can be &lt;a href=&quot;https://kdp.amazon.com/en_US/help/topic/GUGQ4WDZ92F733GC#download&quot;&gt;downloaded from the Amazon website&lt;/a&gt; and supports Windows and Mac. Follow the instructions to install the software and then launch it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When Kindle Create is open, click on the ‘+ Create new’ button, then click ‘Choose’ in the bottom right.&lt;/li&gt;
  &lt;li&gt;Next we need to decide the format. For this example we are using ‘Reflowable’ as it is a standard text based book we want to create. After selecting ‘Reflowable’, click the ‘Continue’ button.&lt;/li&gt;
  &lt;li&gt;Enter the book title. You can also enter the author and publisher if you choose to. Then press ‘Choose File (.doc, .docx, .rtf)’.&lt;/li&gt;
  &lt;li&gt;Select the file we downloaded from Google Docs and choose ‘Open’. It will start the import process. Once it has completed, press the ‘Continue’ button.&lt;/li&gt;
  &lt;li&gt;A new screen comes up saying ‘Automatic chapter titles’. This is where setting the chapter titles as Heading 1 and inserting the Page breaks earlier helps us out. Press the Get started button.&lt;/li&gt;
  &lt;li&gt;Oh no! There are no chapter headings detected??? Don’t panic, press ‘Accept Selected’ and then you will see the ‘Body’ section on the left with the chapter titles.&lt;/li&gt;
  &lt;li&gt;Click on each chapter within Body, and for each you need to click on the chapter text in the editor and then press ‘Chapter title’ within the ‘Elements on page’ section on the right. Go through all chapters and repeat this process.&lt;/li&gt;
  &lt;li&gt;This is optional, but I also like to select the first paragraph of each chapter and click on the ‘Chapter first paragraph’ button in the ‘Elements on page’ section. This gives the first paragraph of each chapter a large letter.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;make-sure-you-press-save&quot;&gt;Make sure you press save&lt;/h2&gt;

&lt;p&gt;We have done a lot of hard work getting this far. Make sure you regularly press save and keep your changes.&lt;/p&gt;

&lt;h2 id=&quot;contents&quot;&gt;Contents&lt;/h2&gt;

&lt;p&gt;Now we have all the chapter titles selected, we can easily generate a ‘Table of contents’ by clicking on the + (plus) symbol next to ‘Front Matter’ and selecting ‘Table of contents’ from the dropdown. This will give you a preview of the contents with your chapter headings. Press ‘Ok’ to create them.&lt;/p&gt;

&lt;h2 id=&quot;front-matter&quot;&gt;Front Matter&lt;/h2&gt;

&lt;p&gt;It’s up to you what front matter you add to your book, but I would recommend adding a title page and copyright as a minimum. If you have a prologue, then you can also add this to the Front Matter section.&lt;/p&gt;

&lt;h2 id=&quot;back-matter&quot;&gt;Back Matter&lt;/h2&gt;

&lt;p&gt;Again, it’s up to you what back matter you want to add to your book, but if you have other books on Kindle then you can add ‘Books by this author’ with the titles and blurb, but also a link to the other books so readers can easily click on the links to buy your other books. This is especially useful if your book is in a series and the reader wants to read the next book.&lt;/p&gt;

&lt;h2 id=&quot;themes&quot;&gt;Themes&lt;/h2&gt;

&lt;p&gt;There are four themes to choose from. Whilst it’s not loads of options, it ensures that it should always look good to readers. Choose a theme that best suits your book. You can preview how the theme looks using the Preview tool.&lt;/p&gt;

&lt;h2 id=&quot;preview&quot;&gt;Preview&lt;/h2&gt;

&lt;p&gt;Kindle Create has a handy ‘Preview’ tool that lets you preview the book in tablet, phone and kindle views. This allows you to double check your formatting before it has been uploaded to Kindle Direct Publishing (KDP). You can easily make changes in the editor and then open the preview again to ensure it looks as you expect.&lt;/p&gt;

&lt;h2 id=&quot;exporting&quot;&gt;Exporting&lt;/h2&gt;

&lt;p&gt;When you are happy with the content and your formatting, you can press the ‘Export’ button.&lt;/p&gt;

&lt;p&gt;You can export to KPF and EPUB formats. I generally use the KPF format as I can use it for both kindle and paperback formats in KDP.&lt;/p&gt;

&lt;p&gt;Once the book has exported, you can create your new book in KDP as normal and upload the .kpf file.&lt;/p&gt;

&lt;p&gt;Only one last thing to say is good luck with your new book!&lt;/p&gt;
</description>
        <pubDate>Tue, 20 May 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/05/20/creating-a-kindle-book-from-google-docs.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/05/20/creating-a-kindle-book-from-google-docs.html</guid>
        
        <category>author</category>
        
        <category>writing</category>
        
        <category>books</category>
        
        
      </item>
    
      <item>
        <title>Hosting a Laravel app with AWS Beanstalk</title>
        <description>&lt;p&gt;There are lots of possible hosting solutions available for Laravel, from Forge, to Vapor to the new Laravel Cloud. I’ll start out by saying that these other solutions are much easier to get up and running than beanstalk, but I thought I’d share some of the “fun” I had getting it up and running.&lt;/p&gt;

&lt;p&gt;If you have to use AWS for hosting, and for whatever reason, you can’t make use of Forge or Vapor, then one potential elastic, scalable solution is AWS Beanstalk.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/elasticbeanstalk/&quot;&gt;AWS Beanstalk&lt;/a&gt; essentially provides you with a number of EC2 (Elastic Cloud Computing) servers that sit behind a load balancer. If demand increases then more instances can be created. If demand drops then the number of instances drops down to save you costs.&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Let’s assume you have an existing Laravel app you want to deploy, but if you don’t then you can create a new Laravel app following the &lt;a href=&quot;https://laravel.com/docs/12.x/installation&quot;&gt;Laravel documentation&lt;/a&gt; and follow along. We also assume you already have an AWS account up and running.&lt;/p&gt;

&lt;p&gt;First you need to install the &lt;a href=&quot;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&quot;&gt;AWS CLI&lt;/a&gt;. The installation method varies per operating system, so follow their guidelines. Once this is installed, I’d recommend setting up the AWS Single Sign On (AWS SSO) and create a profile for your account. For the purposes of this guide, let’s assume your profile is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev-account&lt;/code&gt; going forward.&lt;/p&gt;

&lt;p&gt;Next we need to install the &lt;a href=&quot;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/eb-cli3.html&quot;&gt;Elastic Beanstalk CLI&lt;/a&gt;. Why this isn’t included as part of the AWS CLI I don’t know? Anyway, follow the instructions to install and set up the AWS EB CLI.&lt;/p&gt;

&lt;h2 id=&quot;beanstalk-application&quot;&gt;Beanstalk Application&lt;/h2&gt;

&lt;p&gt;Right, next we want to create an application in Beanstalk. The application is like a project, and each application can have multiple environments, such as production and staging.&lt;/p&gt;

&lt;p&gt;Start by changing directory into your project root, then running the eb init command with the project name and the AWS profile you defined earlier. In this example the application will be called my-laravel-app.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb init my-laravel-app —profile dev-account
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will ask you some questions about your application. When I ran this it saw the package.json file and assumed it was a node.js project. Ensure you say no it isn’t a node project then choose PHP from the list, selecting PHP 8.4 (or the version you wish to use) for your site.&lt;/p&gt;

&lt;p&gt;Ensure you allow SSH from your machine to the EC2 instances and specify a name for the ssh key to use. This will create the key in your .ssh directory.&lt;/p&gt;

&lt;p&gt;When the setup has finished it will should have created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.elasticbeanstalk/config.yml&lt;/code&gt; file with your settings in your project root. This file is added to your .gitignore file so it won’t be committed to your version control.&lt;/p&gt;

&lt;h2 id=&quot;beanstalk-environment-preparation&quot;&gt;Beanstalk Environment Preparation&lt;/h2&gt;

&lt;p&gt;You can proceed to create an environment and deploy your project, but it may not work without some additional configuration. Here is what I added to get my Laravel app to work with AWS Beanstalk.&lt;/p&gt;

&lt;h3 id=&quot;adding-php-extensions&quot;&gt;Adding PHP Extensions&lt;/h3&gt;

&lt;p&gt;I quickly found out that the AWS Linux OS for PHP 8.4 doesn’t have php-zip which I needed for my project. If you need this PHP extension you can create the following file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.platform/hooks/prebuild/install-php-zip.sh&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

yum &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libzip libzip-devel

&lt;span class=&quot;c&quot;&gt;# Will enable zip extension in /etc/php.ini&lt;/span&gt;

pecl upgrade zip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Putting the script in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prebuild&lt;/code&gt; folder tells Beanstalk that you need to run this script before the Laravel app is deployed to the EC2 instance and therefore before the composer install is run.&lt;/p&gt;

&lt;p&gt;More information on platform hooks is available in the &lt;a href=&quot;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/platforms-linux-extend.hooks.html&quot;&gt;AWS beanstalk documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If there are any additional PHP dependencies you need then you can either add them to this file or created separate script files within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prebuild&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3 id=&quot;php-configuration&quot;&gt;PHP Configuration&lt;/h3&gt;

&lt;p&gt;The next file that we need to add before we can create your environment configures some PHP settings and installs some packages. The default setting for Beanstalk EC2 instances is to use the project root as the document root, but we need to set it to the public folder.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ebextensions/composer.config&lt;/code&gt; and paste in the following.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;yum&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;nfs-utils&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;jq&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;amazon-efs-utils&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;npm&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;option_settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;aws:elasticbeanstalk:container:php:phpini&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;document_root&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/public&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;display_errors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Off&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;composer_options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--optimize-autoloader&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This file will install some dependencies for us to use later through the packages list. The amazon-efs-utils is useful if you want to use AWS Elastic File Store (EFS) to store any files. The node and npm dependencies allows you to build your frontend assets.&lt;/p&gt;

&lt;p&gt;The option settings then let us override the default settings. I like to add the optimise autoloader setting when doing composer install on a server.&lt;/p&gt;

&lt;h3 id=&quot;frontend-configuration&quot;&gt;Frontend configuration&lt;/h3&gt;

&lt;p&gt;The last file we need to add will take care of our frontend npm dependencies. We have configured Elastic Beanstalk to use a PHP instance, so it will automatically run composer install for us and install our PHP dependencies, but it doesn’t install and build our frontend dependencies. We have to do this ourselves.&lt;/p&gt;

&lt;p&gt;To tell Beanstalk to install and compile our frontend assets we can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.platform/hooks/postdeploy/npm-dependencies.sh&lt;/code&gt; file with the following config.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /var/www/html

npm &lt;span class=&quot;nb&quot;&gt;install

&lt;/span&gt;npm run production
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postdeploy&lt;/code&gt; folder tells Beanstalk to run this after our code has been deployed to the EC2 instance(s). Add and commit your new files to your git repo.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-environment&quot;&gt;Creating the environment&lt;/h2&gt;

&lt;p&gt;Ok, we are now ready to create an environment. Let’s start with the staging environment, along with a database for our environment.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb create staging &lt;span class=&quot;nt&quot;&gt;--database&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--profile&lt;/span&gt; dev-account
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will prompt you for the database type, the username and password. Make sure you write them down somewhere.&lt;/p&gt;

&lt;p&gt;A quick note about the database. If you create a database as part of your environment, then if you delete the environment it will delete your database too. You can change the settings to decouple the database, or you can create a separate database and manually link it to your environment.&lt;/p&gt;

&lt;p&gt;The environment will take a little while to provision all the resources it needs to get up and running (especially the database). Once it has finished you can then add the environment variables your Laravel app needs. Rather than creating a .env file on the server, you create the variables through the command line or the AWS web console.&lt;/p&gt;

&lt;p&gt;The main variables you will need to add are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;APP_KEY&lt;/li&gt;
  &lt;li&gt;APP_ENV&lt;/li&gt;
  &lt;li&gt;APP_DEBUG&lt;/li&gt;
  &lt;li&gt;DB_CONNECTION&lt;/li&gt;
  &lt;li&gt;DB_DATABASE&lt;/li&gt;
  &lt;li&gt;DB_HOST&lt;/li&gt;
  &lt;li&gt;DB_PASSWORD&lt;/li&gt;
  &lt;li&gt;DB_USERNAME&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AWS tutorial for Laravel and Elastic Beanstalk suggests that you update your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.php&lt;/code&gt; file to match their predefined database variables, such as RDS_HOSTNAME, as these are automatically injected into your environment, but it’s up to you how you want to proceed.&lt;/p&gt;

&lt;p&gt;Once you add and save these variables it will redeploy your environment and your app should now be available to view.&lt;/p&gt;

&lt;p&gt;Sometimes it seems that it doesn’t always run the post build scripts when you edit environment variables. This is very annoying as it means that your frontend assets don’t exist. Sometimes it seems fine, but other times it causes errors. You can manually redeploy the environment using the below:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb deploy staging —profile dev-account
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;running-artisan-scripts&quot;&gt;Running artisan scripts&lt;/h2&gt;

&lt;p&gt;To run artisan scripts, you can ssh into the EC2 instance using the below:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;eb ssh staging —profile dev-account
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have found a couple of issues when running artisan scripts with Elastic Beanstalk.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The environment variables don’t exist to the ec2-user&lt;/li&gt;
  &lt;li&gt;The ec2-user doesn’t have permission to write to the log file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I discovered the first issue when I tried to migrate the database. It stated that the sqlite database doesn’t exist and would I like to create it. I was confused as I had set the environment variables to use mysql and not sqlite.&lt;/p&gt;

&lt;p&gt;After a bit of experimenting I discovered you could set the environment variables to your session by running the below command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; /opt/elasticbeanstalk/bin/get-config &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; YAML environment | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/: /=/g&apos;&lt;/span&gt; | xargs&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This gets the config variables in yaml format, then replaces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; to make it the expected format and then exports them to your session. I’ll be honest, it’s very annoying having to do this.&lt;/p&gt;

&lt;p&gt;For the second issue of the ec2-user not being able to write to the log file, I have found you can su to the webapp user and then run artisan commands using that user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An important disclaimer here! I’m not sure this is the best way of doing things so maybe spend some time to find a better solution to these issues.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;storage&quot;&gt;Storage&lt;/h2&gt;

&lt;p&gt;We briefly mentioned storage earlier. The EC2 instances are designed to be throw away instances that can be easily replaced as well as being load balanced. This means we can’t store any content that isn’t in your version control as they won’t exist in new instances that are created.&lt;/p&gt;

&lt;p&gt;There are a few possible solutions depending on your circumstances, but to summarise some of the options I’ve investigated, you can either use AWS Elastic File System (EFS) and create a mount point on each instance or you can create an S3 bucket and set your filesystem disk to use S3 and add the relevant environment variables needed for S3.&lt;/p&gt;

&lt;p&gt;There is some information on using EFS with Elastic Beanstalk on the &lt;a href=&quot;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/services-efs.html&quot;&gt;AWS documentation site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A little reminder, as per the database, if you create an EFS as part of your Beanstalk environment, then if you delete the environment it will also delete the EFS and all the content within it.&lt;/p&gt;

&lt;h2 id=&quot;configuring-https&quot;&gt;Configuring HTTPS&lt;/h2&gt;

&lt;p&gt;The default configuration sets up the environment to be accessible on port 80, so to allow https you need to update the load balancer configuration and add a listener for port 443. How you do this depends on the type of load balancer you are using and whether you want to terminate HTTPS at the load balancer of the EC2 instances, so consult the &lt;a href=&quot;https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/configuring-https.html&quot;&gt;Elastic Beanstalk documentation&lt;/a&gt; for how to get this up and running.&lt;/p&gt;

&lt;h2 id=&quot;scalability&quot;&gt;Scalability&lt;/h2&gt;

&lt;p&gt;One of the main benefits of Elastic Beanstalk is the elastic nature of the system. You can either create a configuration file to specify the minimum and maximum number of instances or you can do this through the web console. You may want to do this through the web console so you can specify different values for production and staging environments. You pay for what you use, so you probably don’t really want more than you need on your staging environment.&lt;/p&gt;

&lt;p&gt;In the web console, go to your environment and then the configuration section. From here, edit the capacity configuration and set the minimum and maximum instances for your environment and then click the Apply button.&lt;/p&gt;

&lt;p&gt;It will redeploy your environment and create any additional instances you have specified to meet the new minimum. When you get more traffic it will increase the number of EC2 instances up to the maximum you have set.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Hopefully you will now have your staging environment all up and running with AWS Elastic Beanstalk. As you can see by the length of this guide, there is quite a lot of configuration to get things up and running, as well as various annoyances along the way.&lt;/p&gt;

&lt;p&gt;I normally use &lt;a href=&quot;https://vapor.laravel.com&quot;&gt;Laravel Vapor&lt;/a&gt; to host my projects and you can tell it was built to work for Laravel and make the developer experience silky smooth. I am interested to try out &lt;a href=&quot;https://cloud.laravel.com/&quot;&gt;Laravel Cloud&lt;/a&gt; at some point in the future too as that is all about shipping your app.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://stocksnap.io/photo/macbook-computer-JPZDGEMDH3&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/burstshopify&quot;&gt;Burst&lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Apr 2025 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2025/04/07/hosting-a-laravel-app-with-aws-beanstalk.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2025/04/07/hosting-a-laravel-app-with-aws-beanstalk.html</guid>
        
        <category>webdev</category>
        
        <category>laravel</category>
        
        <category>hosting</category>
        
        
      </item>
    
      <item>
        <title>Testing window.open() in JavaScript with Jest</title>
        <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=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/open&quot;&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 id=&quot;the-component-to-test&quot;&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=&quot;https://www.react-hook-form.com/&quot;&gt;react-hook-form&lt;/a&gt; to add validation to your form.&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// MyForm.js&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

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

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

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

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

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

&lt;h2 id=&quot;what-id-normally-test&quot;&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 id=&quot;mocking-windowopen&quot;&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;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Mock window.open&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Test Name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;An example subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Submit (opens in new window)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s worth having a read through the documentation for the &lt;a href=&quot;https://testing-library.com/docs/guide-events&quot;&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=&quot;https://testing-library.com/docs/dom-testing-library/api-async/#waitfor&quot;&gt;waitFor()&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalledTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.com?name=Test%20Name&amp;amp;subject=An%20example%20subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_blank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-complete-test-file&quot;&gt;The complete test file&lt;/h2&gt;

&lt;p&gt;Here is the complete test file for your reference.&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// MyForm.test.js&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;waitFor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@testing-library/react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyForm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./MyForm&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

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

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

    &lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Test Name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;An example subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fireEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;screen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Submit (opens in new window)&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalledTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://example.com?name=Test%20Name&amp;amp;subject=An%20example%20subject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;_blank&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://stocksnap.io/photo/laptop-apple-UOI2HF8SXU&quot;&gt;Photo&lt;/a&gt; by &lt;a href=&quot;https://stocksnap.io/author/41320&quot;&gt;energepic.com &lt;/a&gt; on &lt;a href=&quot;https://stocksnap.io&quot;&gt;StockSnap&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 01 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://www.csrhymes.com/2024/12/01/testing-window-open-in-javascript.html</link>
        <guid isPermaLink="true">https://www.csrhymes.com/2024/12/01/testing-window-open-in-javascript.html</guid>
        
        <category>webdev</category>
        
        <category>javascript</category>
        
        <category>testing</category>
        
        
      </item>
    
  </channel>
</rss>
