The Era Of Platform Primitives Is Finally Here

This article is a sponsored by Netlify

In the past, the web ecosystem moved at a very slow pace. Developers would go years without a new language feature or working around a weird browser quirk. This pushed our technical leaders to come up with creative solutions to circumvent the platform’s shortcomings. We invented bundling, polyfills, and transformation steps to make things work everywhere with less of a hassle.

Slowly, we moved towards some sort of consensus on what we need as an ecosystem. We now have TypeScript and Vite as clear preferences—pushing the needle of what it means to build consistent experiences for the web. Application frameworks have built whole ecosystems on top of them: SolidStart, Nuxt, Remix, and Analog are examples of incredible tools built with such primitives. We can say that Vite and TypeScript are tooling primitives that empower the creation of others in diverse ecosystems.

With bundling and transformation needs somewhat defined, it was only natural that framework authors would move their gaze to the next layer they needed to abstract: the server.

Server Primitives

The UnJS folks have been consistently building agnostic tooling that can be reused in different ecosystems. Thanks to them, we now have frameworks and libraries such as H3 (a minimal Node.js server framework built with TypeScript), which enables Nitro (a whole server runtime powered by Vite, and H3), that in its own turn enabled Vinxi (an application bundler and server runtime that abstracts Nitro and Vite).

Nitro is used already by three major frameworks: Nuxt, Analog, and SolidStart. While Vinxi is also used by SolidStart. This means that any platform which supports one of these, will definitely be able to support the others with zero additional effort.

This is not about taking a bigger slice of the cake. But making the cake bigger for everyone.

Frameworks, platforms, developers, and users benefit from it. We bet on our ecosystem together instead of working in silos with our monolithic solutions. Empowering our developer-users to gain transferable skills and truly choose the best tool for the job with less vendor lock-in than ever before.

Serverless Rejoins Conversation

Such initiatives have probably been noticed by serverless platforms like Netlify. With Platform Primitives, frameworks can leverage agnostic solutions for common necessities such as Incremental Static Regeneration (ISR), Image Optimization, and key/value (kv) storage.

As the name implies, Netlify Platform Primitives are a group of abstractions and helpers made available at a platform level for either frameworks or developers to leverage when using their applications. This brings additional functionality simultaneously to every framework. This is a big and powerful shift because, up until now, each framework would have to create its own solutions and backport such strategies to compatibility layers within each platform.

Moreover, developers would have to wait for a feature to first land on a framework and subsequently for support to arrive in their platform of choice. Now, as long as they’re using Netlify, those primitives are available directly without any effort and time put in by the framework authors. This empowers every ecosystem in a single measure.

Serverless means server infrastructure developers don’t need to handle. It’s not a misnomer, but a format of Infrastructure As A Service.

As mentioned before, Netlify Platform Primitives are three different features:

  1. Image CDN
    A content delivery network for images. It can handle format transformation and size optimization via URL query strings.
  2. Caching
    Basic primitives for their server runtime that help manage the caching directives for browser, server, and CDN runtimes smoothly.
  3. Blobs
    A key/value (KV) storage option is automatically available to your project through their SDK.

Let’s take a quick dive into each of these features and explore how they can increase our productivity with a serverless fullstack experience.

Image CDN

Every image in a /public can be served through a Netlify function. This means it’s possible to access it through a /.netlify/images path. So, without adding sharp or any image optimization package to your stack, deploying to Netlify allows us to serve our users with a better format without transforming assets at build-time. In a SolidStart, in a few lines of code, we could have an Image component that transforms other formats to .webp.

import { type JSX } from "solid-js";

const SITE_URL = "https://example.com";

interface Props extends JSX.ImgHTMLAttributes<HTMLImageElement> {
  format?: "webp" | "jpeg" | "png" | "avif" | "preserve";
  quality?: number | "preserve";
}

const getQuality = (quality: Props["quality"]) => {
  if (quality === "preserve") return"";
  return &q=${quality || "75"};
};

function getFormat(format: Props["format"]) {
  switch (format) {
    case "preserve":
      return"  ";
    case "jpeg":
      return &fm=jpeg;
    case "png":
      return &fm=png;
    case "avif":
      return &fm=avif;
    case "webp":
    default:
      return &fm=webp;
  }
}

export function Image(props: Props) {
  return (
    <img
      {...props}
      src={${SITE_URL}/.netlify/images?url=/${props.src}${getFormat(
        props.format
      )}${getQuality(props.quality)}}
    />
  );
}

Notice the above component is even slightly more complex than bare essentials because we’re enforcing some default optimizations. Our getFormat method transforms images to .webp by default. It’s a broadly supported format that’s significantly smaller than the most common and without any loss in quality. Our get quality function reduces the image quality to 75% by default; as a rule of thumb, there isn’t any perceivable loss in quality for large images while still providing a significant size optimization.

Caching

By default, Netlify caching is quite extensive for your regular artifacts - unless there’s a new deployment or the cache is flushed manually, resources will last for 365 days. However, because server/edge functions are dynamic in nature, there’s no default caching to prevent serving stale content to end-users. This means that if you have one of these functions in production, chances are there’s some caching to be leveraged to reduce processing time (and expenses).

By adding a cache-control header, you already have done 80% of the work in optimizing your resources for best serving users. Some commonly used cache control directives:

{
  "cache-control": "public, max-age=0, stale-while-revalidate=86400"

}
  • public: Store in a shared cache.
  • max-age=0: resource is immediately stale.
  • stale-while-revalidate=86400: if the cache is stale for less than 1 day, return the cached value and revalidate it in the background.
{
  "cache-control": "public, max-age=86400, must-revalidate"

}
  • public: Store in a shared cache.
  • max-age=86400: resource is fresh for one day.
  • must-revalidate: if a request arrives when the resource is already stale, the cache must be revalidated before a response is sent to the user.

Note: For more extensive information about possible compositions of Cache-Control directives, check the mdn entry on Cache-Control.

The cache is a type of key/value storage. So, once our responses are set with proper cache control, platforms have some heuristics to define what the key will be for our resource within the cache storage. The Web Platform has a second very powerful header that can dictate how our cache behaves.

The Vary response header is composed of a list of headers that will affect the validity of the resource (method and the endpoint URL are always considered; no need to add them). This header allows platforms to define other headers defined by location, language, and other patterns that will define for how long a response can be considered fresh.

The Vary response header is a foundational piece of a special header in Netlify Caching Primitive. The Netlify-Vary will take a set of instructions on which parts of the request a key should be based. It is possible to tune a response key not only by the header but also by the value of the header.

  • query: vary by the value of some or all request query parameters.
  • header: vary by the value of one or more request headers.
  • language: vary by the languages from the Accept-Language header.
  • country: vary by the country inferred from a GeoIP lookup on the request IP address.
  • cookie: vary by the value of one or more request cookie keys.

This header offers strong fine-control over how your resources are cached. Allowing for some creative strategies to optimize how your app will perform for specific users.

Blob Storage

This is a highly-available key/value store, it’s ideal for frequent reads and infrequent writes. They’re automatically available and provisioned for any Netlify Project.

It’s possible to write on a blob from your runtime or push data for a deployment-specific store. For example, this is how an Action Function would register a number of likes in store with SolidStart.

import { getStore } from "@netlify/blobs";
import { action } from "@solidjs/router";

export const upVote = action(async (formData: FormData) => {
  "use server";

  const postId = formData.get("id");
  const postVotes = formData.get("votes");

  if (typeof postId !== "string" || typeof postVotes !== "string") return;

  const store = getStore("posts");
  const voteSum = Number(postVotes) + 1)

  await store.set(postId, String(voteSum);

  console.log("done");
  return voteSum

});
Final Thoughts

With high-quality primitives, we can enable library and framework creators to create thin integration layers and adapters. This way, instead of focusing on how any specific platform operates, it will be possible to focus on the actual user experience and practical use-cases for such features. Monoliths and deeply integrated tooling make sense to build platforms fast with strong vendor lock-in, but that’s not what the community needs. Betting on the web platform is a more sensible and future-friendly way.

Let me know in the comments what your take is about unbiased tooling versus opinionated setups!

Gatsby Headaches: Working With Media (Part 1)

Working with media files in Gatsby might not be as straightforward as expected. I remember starting my first Gatsby project. After consulting Gatsby’s documentation, I discovered I needed to use the gatsby-source-filesystem plugin to make queries for local files. Easy enough!

That’s where things started getting complicated. Need to use images? Check the docs and install one — or more! — of the many, many plugins available for handling images. How about working with SVG files? There is another plugin for that. Video files? You get the idea.

It’s all great until any of those plugins or packages become outdated and go unmaintained. That’s where the headaches start.

If you are unfamiliar with Gatsby, it’s a React-based static site generator that uses GraphQL to pull structured data from various sources and uses webpack to bundle a project so it can then be deployed and served as static files. It’s essentially a static site generator with reactivity that can pull data from a vast array of sources.

Like many static site frameworks in the Jamstack, Gatsby has traditionally enjoyed a great reputation as a performant framework, although it has taken a hit in recent years. Based on what I’ve seen, however, it’s not so much that the framework is fast or slow but how the framework is configured to handle many of the sorts of things that impact performance, including media files.

So, let’s solve the headaches you might encounter when working with media files in a Gatsby project. This article is the first of a brief two-part series where we will look specifically at the media you are most likely to use: images, video, and audio. After that, the second part of this series will get into different types of files, including Markdown, PDFs, and even 3D models.

Solving Image Headaches In Gatsby

I think that the process of optimizing images can fall into four different buckets:

  1. Optimize image files.
    Minimizing an image’s file size without losing quality directly leads to shorter fetching times. This can be done manually or during a build process. It’s also possible to use a service, like Cloudinary, to handle the work on demand.
  2. Prioritize images that are part of the First Contentful Paint (FCP).
    FCP is a metric that measures the time between the point when a page starts loading to when the first bytes of content are rendered. The idea is that fetching assets that are part of that initial render earlier results in faster loading rather than waiting for other assets lower on the chain.
  3. Lazy loading other images.
    We can prevent the rest of the images from render-blocking other assets using the loading="lazy" attribute on images.
  4. Load the right image file for the right context.
    With responsive images, we can serve one version of an image file at one screen size and serve another image at a different screen size with the srcset and sizes attributes or with the <picture> element.

These are great principles for any website, not only those built with Gatsby. But how we build them into a Gatsby-powered site can be confusing, which is why I’m writing this article and perhaps why you’re reading it.

Lazy Loading Images In Gatsby

We can apply an image to a React component in a Gatsby site like this:

import * as React from "react";

import forest from "./assets/images/forest.jpg";

const ImageHTML = () => {
  return <img src={ forest } alt="Forest trail" />;
};

It’s important to import the image as a JavaScript module. This lets webpack know to bundle the image and generate a path to its location in the public folder.

This works fine, but when are we ever working with only one image? What if we want to make an image gallery that contains 100 images? If we try to load that many <img> tags at once, they will certainly slow things down and could affect the FCP. That’s where the third principle that uses the loading="lazy" attribute can come into play.

import * as React from "react";

import forest from "./assets/images/forest.jpg";

const LazyImageHTML = () => {
  return <img src={ forest } loading="lazy" alt="Forest trail" />;
};

We can do the opposite with loading="eager". It instructs the browser to load the image as soon as possible, regardless of whether it is onscreen or not.

import * as React from "react";

import forest from "./assets/images/forest.jpg";

const EagerImageHTML = () => {
  return <img src={ forest } loading="eager" alt="Forest trail" />;
};

Implementing Responsive Images In Gatsby

This is a basic example of the HTML for responsive images:

<img
  srcset="./assets/images/forest-400.jpg 400w, ./assets/images/forest-800.jpg 800w"
  sizes="(max-width: 500px) 400px, 800px"
  alt="Forest trail"
/>

In Gatsby, we must import the images first and pass them to the srcset attribute as template literals so webpack can bundle them:

import * as React from "react";

import forest800 from "./assets/images/forest-800.jpg";

import forest400 from "./assets/images/forest-400.jpg";

const ResponsiveImageHTML = () => {
  return (
    <img
      srcSet={`

        ${ forest400 } 400w,

        ${ forest800 } 800w

      `}
      sizes="(max-width: 500px) 400px, 800px"
      alt="Forest trail"
    />
  );
};

That should take care of any responsive image headaches in the future.

Loading Background Images In Gatsby

What about pulling in the URL for an image file to use on the CSS background-url property? That looks something like this:

import * as React from "react";

import "./style.css";

const ImageBackground = () => {
  return <div className="banner"></div>;
};
/* style.css */

.banner {
      aspect-ratio: 16/9;
      background-size: cover;

    background-image: url("./assets/images/forest-800.jpg");

  /* etc. */
}

This is straightforward, but there is still room for optimization! For example, we can do the CSS version of responsive images, which loads the version we want at specific breakpoints.

/* style.css */

@media (max-width: 500px) {
  .banner {
    background-image: url("./assets/images/forest-400.jpg");
  }
}

Using The gatsby-source-filesystem Plugin

Before going any further, I think it is worth installing the gatsby-source-filesystem plugin. It’s an essential part of any Gatsby project because it allows us to query data from various directories in the local filesystem, making it simpler to fetch assets, like a folder of optimized images.

npm i gatsby-source-filesystem

We can add it to our gatsby-config.js file and specify the directory from which we will query our media assets:

// gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,

      options: {
        name: `assets`,

        path: `${ __dirname }/src/assets`,
      },
    },
  ],
};

Remember to restart your development server to see changes from the gatsby-config.js file.

Now that we have gatsby-source-filesystem installed, we can continue solving a few other image-related headaches. For example, the next plugin we look at is capable of simplifying the cures we used for lazy loading and responsive images.

Using The gatsby-plugin-image Plugin

The gatsby-plugin-image plugin (not to be confused with the outdated gatsby-image plugin) uses techniques that automatically handle various aspects of image optimization, such as lazy loading, responsive sizing, and even generating optimized image formats for modern browsers.

Once installed, we can replace standard <img> tags with either the <GatsbyImage> or <StaticImage> components, depending on the use case. These components take advantage of the plugin’s features and use the <picture> HTML element to ensure the most appropriate image is served to each user based on their device and network conditions.

We can start by installing gatsby-plugin-image and the other plugins it depends on:

npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp

Let’s add them to the gatsby-config.js file:

// gatsby-config.js

module.exports = {
plugins: [

// other plugins
`gatsby-plugin-image`,
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`],

};

This provides us with some features we will put to use a bit later.

Using The StaticImage Component

The StaticImage component serves images that don’t require dynamic sourcing or complex transformations. It’s particularly useful for scenarios where you have a fixed image source that doesn’t change based on user interactions or content updates, like logos, icons, or other static images that remain consistent.

The main attributes we will take into consideration are:

  • src: This attribute is required and should be set to the path of the image you want to display.
  • alt: Provides alternative text for the image.
  • placeholder: This attribute can be set to either blurred or dominantColor to define the type of placeholder to display while the image is loading.
  • layout: This defines how the image should be displayed. It can be set to fixed for, as you might imagine, images with a fixed size, fullWidth for images that span the entire container, and constrained for images scaled down to fit their container.
  • loading: This determines when the image should start loading while also supporting the eager and lazy options.

Using StaticImage is similar to using a regular HTML <img> tag. However, StaticImage requires passing the string directly to the src attribute so it can be bundled by webpack.

import * as React from "react";

import { StaticImage } from "gatsby-plugin-image";

const ImageStaticGatsby = () => {
  return (
    <StaticImage
      src="./assets/images/forest.jpg"
      placeholder="blurred"
      layout="constrained"
      alt="Forest trail"
      loading="lazy"
    />
  );
  };

The StaticImage component is great, but you have to take its constraints into account:

  • No Dynamically Loading URLs
    One of the most significant limitations is that the StaticImage component doesn’t support dynamically loading images based on URLs fetched from data sources or APIs.
  • Compile-Time Image Handling
    The StaticImage component’s image handling occurs at compile time. This means that the images you specify are processed and optimized when the Gatsby site is built. Consequently, if you have images that need to change frequently based on user interactions or updates, the static nature of this component might not fit your needs.
  • Limited Transformation Options
    Unlike the more versatile GatsbyImage component, the StaticImage component provides fewer transformation options, e.g., there is no way to apply complex transformations like cropping, resizing, or adjusting image quality directly within the component. You may want to consider alternative solutions if you require advanced transformations.

Using The GatsbyImage Component

The GatsbyImage component is a more versatile solution that addresses the limitations of the StaticImage component. It’s particularly useful for scenarios involving dynamic image loading, complex transformations, and advanced customization.

Some ideal use cases where GatsbyImage is particularly useful include:

  • Dynamic Image Loading
    If you need to load images dynamically based on data from APIs, content management systems, or other sources, the GatsbyImage component is the go-to choice. It can fetch images and optimize their loading behavior.
  • Complex transformations
    The GatsbyImage component is well-suited for advanced transformations, using GraphQL queries to apply them.
  • Responsive images
    For responsive design, the GatsbyImage component excels by automatically generating multiple sizes and formats of an image, ensuring that users receive an appropriate image based on their device and network conditions.

Unlike the StaticImage component, which uses a src attribute, GatsbyImage has an image attribute that takes a gatsbyImageData object. gatsbyImageData contains the image information and can be queried from GraphQL using the following query.

query {
  file(name: { eq: "forest" }) {
    childImageSharp {
      gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)
    }

    name
  }
}

If you’re following along, you can look around your Gatsby data layer at http://localhost:8000/___graphql.

From here, we can use the useStaticQuery hook and the graphql tag to fetch data from the data layer:

import * as React from "react";

import { useStaticQuery, graphql } from "gatsby";

import { GatsbyImage, getImage } from "gatsby-plugin-image";

const ImageGatsby = () => {
  // Query data here:

  const data = useStaticQue(graphql``);

  return <div></div>;
};

Next, we can write the GraphQL query inside of the graphql tag:

import * as React from "react";

import { useStaticQuery, graphql } from "gatsby";

const ImageGatsby = () => {
  const data = useStaticQuery(graphqlquery {
      file(name: { eq: "forest" }) {
        childImageSharp {
          gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)
        }

        name
      }
    });

  return <div></div>;
};

Next, we import the GatsbyImage component from gatsby-plugin-image and assign the image’s gatsbyImageData property to the image attribute:

import * as React from "react";

import { useStaticQuery, graphql } from "gatsby";

import { GatsbyImage } from "gatsby-plugin-image";

const ImageGatsby = () => {
  const data = useStaticQuery(graphqlquery {
      file(name: { eq: "forest" }) {
        childImageSharp {
          gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)
        }

        name
      }
    });

  return <GatsbyImage image={ data.file.childImageSharp.gatsbyImageData } alt={ data.file.name } />;
};

Now, we can use the getImage helper function to make the code easier to read. When given a File object, the function returns the file.childImageSharp.gatsbyImageData property, which can be passed directly to the GatsbyImage component.

import * as React from "react";

import { useStaticQuery, graphql } from "gatsby";

import { GatsbyImage, getImage } from "gatsby-plugin-image";

const ImageGatsby = () => {
  const data = useStaticQuery(graphqlquery {
      file(name: { eq: "forest" }) {
        childImageSharp {
          gatsbyImageData(width: 800, placeholder: BLURRED, layout: CONSTRAINED)
        }

        name
      }
    });

  const image = getImage(data.file);

  return <GatsbyImage image={ image } alt={ data.file.name } />;
};

Using The gatsby-background-image Plugin

Another plugin we could use to take advantage of Gatsby’s image optimization capabilities is the gatsby-background-image plugin. However, I do not recommend using this plugin since it is outdated and prone to compatibility issues. Instead, Gatsby suggests using gatsby-plugin-image when working with the latest Gatsby version 3 and above.

If this compatibility doesn’t represent a significant problem for your project, you can refer to the plugin’s documentation for specific instructions and use it in place of the CSS background-url usage I described earlier.

Solving Video And Audio Headaches In Gatsby

Working with videos and audio can be a bit of a mess in Gatsby since it lacks plugins for sourcing and optimizing these types of files. In fact, Gatsby’s documentation doesn’t name or recommend any official plugins we can turn to.

That means we will have to use vanilla methods for videos and audio in Gatsby.

Using The HTML video Element

The HTML video element is capable of serving different versions of the same video using the <source> tag, much like the img element uses the srset attribute to do the same for responsive images.

That allows us to not only serve a more performant video format but also to provide a fallback video for older browsers that may not support the bleeding edge:

import * as React from "react";

import natureMP4 from "./assets/videos/nature.mp4";

import natureWEBM from "./assets/videos/nature.webm";

const VideoHTML = () => {
  return (
    <video controls>
      <source src={ natureMP4 } type="video/mp4" />

      <source src={ natureWEBM } type="video/webm" />
    </video>
  );
};

P;

We can also apply lazy loading to videos like we do for images. While videos do not support the loading="lazy" attribute, there is a preload attribute that is similar in nature. When set to none, the attribute instructs the browser to load a video and its metadata only when the user interacts with it. In other words, it’s lazy-loaded until the user taps or clicks the video.

We can also set the attribute to metadata if we want the video’s details, such as its duration and file size, fetched right away.

<video controls preload="none">
  <source src={ natureMP4 } type="video/mp4" />

  <source src={ natureWEBM } type="video/webm" />
</video>

Note: I personally do not recommend using the autoplay attribute since it is disruptive and disregards the preload attribute, causing the video to load right away.

And, like images, display a placeholder image for a video while it is loading with the poster attribute pointing to an image file.

<video controls preload="none" poster={ forest }>
  <source src={ natureMP4 } type="video/mp4" />

  <source src={ natureWEBM } type="video/webm" />
</video>

Using The HTML audio Element

The audio and video elements behave similarly, so adding an audio element in Gatsby looks nearly identical, aside from the element:

import * as React from "react";

import audioSampleMP3 from "./assets/audio/sample.mp3";

import audioSampleWAV from "./assets/audio/sample.wav";

const AudioHTML = () => {
  return (
    <audio controls>
      <source src={ audioSampleMP3 } type="audio/mp3" />

      <source src={ audioSampleWAV } type="audio/wav" />
    </audio>
  );
};

As you might expect, the audio element also supports the preload attribute:

<audio controls preload="none">
  <source src={ audioSampleMP3 } type="audio/mp3" />

  <source src={a udioSampleWAV } type="audio/wav" />
</audio>

This is probably as good as we can do to use videos and images in Gatsby with performance in mind, aside from saving and compressing the files as best we can before serving them.

Solving iFrame Headaches In Gatsby

Speaking of video, what about ones embedded in an <iframe> like we might do with a video from YouTube, Vimeo, or some other third party? Those can certainly lead to performance headaches, but it’s not as we have direct control over the video file and where it is served.

Not all is lost because the HTML iframe element supports lazy loading the same way that images do.

import * as React from "react";

const VideoIframe = () => {
  return (
    <iframe
      src="https://www.youtube.com/embed/jNQXAC9IVRw"
      title="Me at the Zoo"
      allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
      allowFullScreen
      loading="lazy"
    />
  );
};

Embedding a third-party video player via iframe can possibly be an easier path than using the HTML video element. iframe elements are cross-platform compatible and could reduce hosting demands if you are working with heavy video files on your own server.

That said, an iframe is essentially a sandbox serving a page from an outside source. They’re not weightless, and we have no control over the code they contain. There are also GDPR considerations when it comes to services (such as YouTube) due to cookies, data privacy, and third-party ads.

Solving SVG Headaches In Gatsby

SVGs contribute to improved page performance in several ways. Their vector nature results in a much smaller file size compared to raster images, and they can be scaled up without compromising quality. And SVGs can be compressed with GZIP, further reducing file sizes.

That said, there are several ways that we can use SVG files. Let’s tackle each one in the contact of Gatsby.

Using Inline SVG

SVGs are essentially lines of code that describe shapes and paths, making them lightweight and highly customizable. Due to their XML-based structure, SVG images can be directly embedded within the HTML <svg> tag.

import * as React from "react";



const SVGInline = () => {

  return (

    <svg viewBox="0 0 24 24" fill="#000000">

      <!-- etc. -->

    </svg>

  );

};

Just remember to change certain SVG attributes, such as xmlns:xlink or xlink:href, to JSX attribute spelling, like xmlnsXlink and xlinkHref, respectively.

Using SVG In img Elements

An SVG file can be passed into an img element's src attribute like any other image file.

import * as React from "react";

import picture from "./assets/svg/picture.svg";

const SVGinImg = () => {
  return <img src={ picture } alt="Picture" />;
};

Loading SVGs inline or as HTML images are the de facto approaches, but there are React and Gatsby plugins capable of simplifying the process, so let’s look at those next.

Inlining SVG With The react-svg Plugin

react-svg provides an efficient way to render SVG images as React components by swapping a ReactSVG component in the DOM with an inline SVG.

Once installing the plugin, import the ReactSVG component and assign the SVG file to the component’s src attribute:

import * as React from "react";

import { ReactSVG } from "react-svg";

import camera from "./assets/svg/camera.svg";

const SVGReact = () => {
  return <ReactSVG src={ camera } />;
};

Using The gatsby-plugin-react-svg Plugin

The gatsby-plugin-react-svg plugin adds svg-react-loader to your Gatsby project’s webpack configuration. The plugin adds a loader to support using SVG files as React components while bundling them as inline SVG.

Once the plugin is installed, add it to the gatsby-config.js file. From there, add a webpack rule inside the plugin configuration to only load SVG files ending with a certain filename, making it easy to split inline SVGs from other assets:

// gatsby-config.js

module.exports = {
  plugins: [
    {
      resolve: "gatsby-plugin-react-svg",

      options: {
        rule: {
          include: /\.inline\.svg$/,
        },
      },
    },
  ],
};

Now we can import SVG files like any other React component:

import * as React from "react";

import Book from "./assets/svg/book.inline.svg";

const GatsbyPluginReactSVG = () => {
  return <Book />;
};

And just like that, we can use SVGs in our Gatsby pages in several different ways!

Conclusion

Even though I personally love Gatsby, working with media files has given me more than a few headaches.

As a final tip, when needing common features such as images or querying from your local filesystem, go ahead and install the necessary plugins. But when you need a minor feature, try doing it yourself with the methods that are already available to you!

If you have experienced different headaches when working with media in Gatsby or have circumvented them with different approaches than what I’ve covered, please share them! This is a big space, and it’s always helpful to see how others approach similar challenges.

Again, this article is the first of a brief two-part series on curing headaches when working with media files in a Gatsby project. The following article will be about avoiding headaches when working with different media files, including Markdown, PDFs, and 3D models.

Further Reading

Easy SVG Customization And Animation: A Practical Guide

Scalable Vector Graphics (SVG) have been a staple in Web Development for quite some time, and for a good reason. They can be scaled up or down without loss of quality due to their vector properties. They can be compressed and optimized due to the XML format. They can also be easily edited, styled, animated, and changed programmatically.

At the end of the day, SVG is a markup language. And just as we can use CSS and JavaScript to enhance our HTML, we can use them the same on SVGs. We could add character and flourishes to our graphic elements, add interactions, and shape truly delightful and memorable user experiences. This optional but crucial detail is often overlooked when building projects, so SVGs end up somewhat underutilized beyond their basic graphical use cases.

How can we even utilize SVGs beyond just using them statically in our projects?

Take the “The State of CSS 2021” landing page, for example. This SVG Logo has been beautifully designed and animated by Christopher Kirk-Nielsen. Although this logo would have looked alright just as a static image, it wouldn’t have had as much of an impact and drawn attention without this intricate animation.

Let’s go even further — SVG, HTML, CSS, and JavaScript can be combined and used to create delightful, interactive, and stunning projects. Check out Sarah Drasner’s incredible work. She has also written a book and has a video course on the topic.

Let’s add it to our HTML and create a simple button component.

<button type="button">
  <svg width="24" height="24" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="..." fill="#C2CCDE" /></svg>
  Add to favorites
</button>

Our button already has some background and text color styles applied to it so let’s see what happens when we add our SVG star icon to it.

Our SVG icon has a fill property applied to it, more specifically, a fill="#C2CCDE" in SVG’s path element. This icon could have come from the SVG library or even exported from a design file, so it makes sense for a color to be exported alongside other graphical properties.

SVG elements can be targeted by CSS like any HTML element, so developers usually reach for the CSS and override the fill color.

.button svg * {
  fill: var(--color-text);
}

However, this is not an ideal solution as this is a greedy selector, and overriding the fill attribute on all elements can have unintended consequences, depending on the SVG markup. Also, fill is not the only property that affects the element’s color.

Let’s showcase this downside by creating a new button and adding a Google logo icon. SVG markup is a bit more complex than our star icon, as it has multiple path elements. SVG elements don’t have to be all visible, there are cases when we want to use them in different ways (as a clipping region, for example), but we won’t go into that. Just keep in mind that greedy selectors that target SVG elements and override their fill properties can produce unexpected results.

 <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
  <path d="..." fill="#4285F4" />
  <path d="..." fill="#34A853" />
  <path d="..." fill="#FBBC05" />
  <path d="..." fill="#EA4335" />
  <path d="..." fill="none" />
 </svg>

We can look at the issue from a different perspective. Instead of looking for a silver bullet CSS solution, we can simply edit our SVG. We already know that the fill property affects the SVG element’s color so let’s see what we can do to make our icons more customizable.

Let’s use a very underutilized CSS value: currentColor. I’ve talked about this awesome value in one of my previous articles.

Often referred to as “the first CSS variable,” currentColor is a value equal to the element’s color property. It can be used to assign a value equal to the value of the color property to any CSS property which accepts a color value. It forces a CSS property to inherit the value of the color property.

If you are looking for more, CSS-Tricks keeps a comprehensive list of various SVG optimization tools with plenty of information and articles on the topic.

Using SVGs With Popular JavaScript-Based Frameworks

Many popular JavaScript frameworks like React have fully integrated SVG in their toolchains to make the developer experience easier. In React, this could be as simple as importing the SVG as a component, and the toolkit would do all the heavy lifting optimizing it.

import React from 'react';
import {ReactComponent as ReactLogo} from './logo.svg';

const App = () => {
  return (
    <div className="App">
      <ReactLogo />
    </div>
  );
}
export default App;

However, as Jason Miller and many other developers have noted, including the SVG markup in JSX bloats the JavaScript bundle and makes the SVG less performant as a result. Instead of just having the browser parse and render an SVG, with JSX, we have expensive extra steps added to the browser. Remember, JavaScript is the most expensive Web resource, and by injecting SVG markup into JSX, we’ve made SVG as expensive as well.

One solution would be to create SVG symbol objects and include them with SVG use. That way, we’ll be defining the SVG icon library in HTML, and we can instantiate it and customize it in React as much as we need to.

<!-- Definition -->
<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
  <symbol id="myIcon" width="24" height="24" viewBox="0 0 24 24">
      <!-- ... -->
  </symbol>
  <!-- ... -->
</svg>

<!-- Usage -->
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <use href="#myIcon" />
</svg>
Breathing Life Into SVGs

Animating SVGs can be easy and fun. It takes just a few minutes to create some simple and effective animations and interactions. If you are unsure which animation would be ideal for a graphic or should you animate it at all, it’s best to consult with the designer. You can even look for some similar examples and use cases on Dribble or other similar websites.

It’s also important to keep in mind that animations should be tasteful, add to the overall user experience, and serve some purpose (draw the user’s attention, for example).

We’ll cover various use cases that you might encounter on your projects. Let’s start with a really sweet example.

Animating A Cookie Banner

Some years ago, I was working on a project where a designer made an adorable cookie graphic for an unobtrusive cookie consent popup to make the element more prominent. This cookie graphic was whimsical and a bit different from the general design of the website.

I’ve created the element and added the graphic, but when looking at the page as a whole, it felt kind of lifeless, and it didn’t stand out as much as we thought it will. The user needed to accept cookies as the majority of website functionality depended on cookies. We wanted to create an unobtrusive banner that doesn’t block user navigation from the outset, so I decided to animate it to make it more prominent and add a bit of flourish and character.

I’ve decided to create three animations that’ll be applied to the cookie SVG:

  • Quick and snappy rolling fade-in entry animation;
  • Repeated wiggle animation with a good amount of delay in between;
  • Repeating and subtle eye sparkle animation.

Here’s the final result of the element that we’ll be creating. We’ll cover each animation step by step.

Let’s store it in a CSS variable so that we can reuse it for the repeatable wiggle movement animation.

--transition-bounce: cubic-bezier(0.2, 0.7, 0.4, 1.65);

Let’s put everything together, set a duration value and fill-mode, and add the animation to our svg element.

/* Our SVG element */
.cookie-notice__graphic {
  opacity: 0; /* Should not be visible at the start */
  animation: enter 0.8s var(--transition-bounce) forwards;
}

Let’s check out what we’ve created. It already looks really nice. Notice how the bouncing easing function made a lot of difference to the overall look and feel of the whole element.

@keyframes wiggle {
  /* Stands still */
  0% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }
  /* Starts moving */
  45% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }

  /* Pulls back */
  50% {
    transform: translate3d(-10%, 0, 0) rotateZ(8deg);
  }

  /* Moves forward */
  55% {
    transform: translate3d(6%, 0, 0) rotateZ(24deg);
  }

  /* Returns to starting position */
  60% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }

  /* Stands still */
  100% {
    transform: translate3d(0, 0, 0) rotateZ(17deg);
  }
}
/* Our SVG element */
.cookie-notice__graphic {
  opacity: 0;
  animation: enter 0.8s var(--transition-bounce) forwards,
    wiggle 6s 3s var(--transition-bounce) infinite;
}

SVG elements can have a CSS class attribute, so we’ll use that to target them. Let’s add the class attribute to the two path elements that we identified.

<!-- ... -->
<path fill="#351f17" d="..." />
<path class="cookie__eye" fill="#fff" d="..." />
<path fill="#351f17" d="..." />
<path class="cookie__eye" fill="#fff" d="..." />
<!-- ... -->

We want to make cookie’s eyes sparkle. I got this idea from a music video for a song by Devin Townsend. You can see the animation play at the 5-minute mark. It just goes to show how you can find ideas pretty much anywhere.

Let’s just change the scale and opacity. Notice how so far, we’ve relied only on those two attributes for all three animations, which are quite different from each other.

@keyframes sparkle {
  from {
    opacity: 0.95;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

We want this animation to repeat without delay. It should be subtle enough to blend in nicely with the graphic and the overall element and not obtrusive for the user. As for the easing function, we’ll do something different. We’ll use staircase functions to achieve that quick and snappy transition between the two animation states (our from and to values).

We need to be careful here. Transform origin is going to be set relative to the parent SVG element’s viewbox and not the element itself. So if we set transform-origin: center center, the transformation will use the center coordinates of the parent SVG and not the path element. We can easily fix that by setting a transform-box property to fill-box.

The nearest SVG viewport is used as the reference box. If a viewBox attribute is specified for the SVG viewport creating element, the reference box is positioned at the origin of the coordinate system established by the viewBox attribute, and the dimension of the reference box is set to the width and height values of the viewBox attribute.
.cookie__eye {
  animation: sparkle 0.15s 1s steps(2, jump-none) infinite alternate;
  transform-box: fill-box;
  transform-origin: center center;
}

Last but not least, let’s respect the user’s accessibility preferences and turn off all animations if they have it set.

@media (prefers-reduced-motion: reduce) {
  *,
  ::before,
  ::after {
    animation-delay: -1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    background-attachment: initial !important;
    scroll-behavior: auto !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
  }
}

Here is the final result. Feel free to play around with the demo and experiment with keyframe values and easing values to change the look and feel of the animation.

Let’s take a closer look at the SVG we’ll be working with. It consists of a few dozen circle elements.

<!-- ... -->
<circle cx="103.5" cy="34.5" r="11.3"></circle>
<circle cx="172.5" cy="34.5" r="15.7"></circle>
<circle cx="310.5" cy="34.5" r="24.6"></circle>
<circle cx="517.5" cy="34.5" r="34.5"></circle>
<circle cx="586.5" cy="34.5" r="34.5"></circle>
<circle cx="655.5" cy="34.5" r="33.4"></circle>
<!-- ... -->

Let’s start by adding a bit of opacity to our background and making it more chaotic. When we apply CSS transforms to elements inside SVG, they are transformed relative to the SVG’s main viewbox. That is why we’re getting a slightly chaotic displacement when applying a scale transform. We’ll use that to our advantage and not change the reference box.

To make things a little bit easier for us, we’ll use SASS. If you are unfamiliar with SASS and SCSS, you can view compiled CSS in CodePen below.

svg circle {
  opacity: 0.85;

  &:nth-child(2n) {
    transform: scale3d(0.75, 0.75, 0.75);
    opacity: 0.3;
}

With that in mind, let’s add some keyframes. We’ll use two sets of keyframes that we’ll apply randomly to our circle elements. Once again, we’ll leverage the scale transform displacement and change the opacity value.

@keyframes a {
  0% {
    opacity: 0.8;
    transform: scale3d(1, 1, 1);
  }
  100% {
    opacity: 0.3;
    transform: scale3d(0.75, 0.75, 0.75);
  }
}

@keyframes b {
  0% {
    transform: scale3d(0.75, 0.75 0.75);
    opacity: 0.3;
  }
  100% {
    opacity: 0.8;
    transform: scale3d(1, 1, 1);
  }
}

Now, let’s use quite a few :nth-child selectors. Every odd child will use the a keyframes, while every even circle will use a b keyframes. We’ll use :nth-child selectors to play around with animation duration and animation delay values.

svg circle {
  opacity: 0.85;
  animation: a 10s cubic-bezier(0.45,0.05,0.55,0.95) alternate infinite;

  &:nth-child(2n) {
    transform: scale3d(0.75, 0.75, 0.75);
    opacity: 0.3;

    animation-name: b;
    animation-duration: 6s;
    animation-delay: 0.5s;
  }

  &:nth-child(3n) {
    animation-duration: 4s;
    animation-delay: 0.25s;
  }

  /* ... */
}

And, once again, just by playing around with opacity values and CSS transforms on our SVG and playing around with child selectors and animation parameters, we’ve managed to create a more interesting background for our hero container.

Here is a markup for our circle SVG.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="50" fill-opacity=".03"/></svg>

Be careful not to inline too much data with base64, so stylesheets can be downloaded and parsed quickly. When we convert it to base64, we get this handy CSS background-image snippet:

background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbC1vcGFjaXR5PSIuMDMiLz48L3N2Zz4=);

We can simply apply a simple animation where we offset the background-position by the background-size value and get this neat background animation.

.wrapper {
  animation: move-background 3.5s linear;
  background-image: url(data:image/svg+xml;base64,...);
  background-size: 96px;
  background-color: #16a757;
  /* ... */
}

@keyframes move-background {
  from {
    background-position: 0 0;
  }

  to {
    background-position: 96px 0;
  }
}

Our example looks more interesting with this subtle moving animation going on in the background. Remember to respect users’ accessibility preferences and turn off the animations if they have a preference set.

Before diving into the animation, we need to cover two SVG properties that we’ll be using: stroke-dasharray and stroke-dashoffset. They’re integral for pulling off this animation.

Stroke can be converted to dashes with a certain length using a stroke-dasharray property.

And we can offset the positions of those strokes by a certain amount using the stroke-dashoffset property.

So, what’s this have to do with our drawing and erasing animation? Imagine what would happen if we could have a dash that covers the whole stroke length and offset it by the same value. In that case, the starting point of the stroke would be way past the ending point of the stroke, and we wouldn’t see it.

svg path {
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 800;  /* Dash covering the whole stroke */
  stroke-dashoffset: 800; /* Offset it to make it invisible */
}

If we animate the offset value from that value back to 0, the stroke would slowly become visible, as it was drawing itself.

svg path {
  /* ... */
  animation: draw 6s linear infinite;
}

@keyframes draw{
  to {
    stroke-dashoffset: 0; /* Reduce offset to make it visible */
  }
}

If we continue to animate the offset value from 0 to a negative value, we’d get the erasing effect.

svg path {
  /* ... */
  animation: drawAndErase 6s linear infinite;
}

@keyframes drawAndErase {
  to {
    stroke-dashoffset: -800;
  }
}

You’re probably wondering where the magical 800 pixel value came from. This value depends on the SVG and the length of the dash needed to cover the whole stroke length. It can be easily guessed, but Chris Coyier has a handy function that can do it for you. However, depending on the stroke properties and SVG shape, this function might not always return an ideal value, but it can guide you closer to it.

Check out the complete demo and feel free to play around with values to see how the stroke properties affect the animation. If you are looking for more examples, CodyHouse has covered a fun-looking button animation using the same trick.

Let’s start by adding the mouse-tracking eye animation. We’ll skip manually implementing this feature in JavaScript and use a handy library called watching-you.

Using the browser’s inspect element tool, we’ll find the target elements inside the SVG and add the eye-left and eye-right CSS classes to these elements, respectively.

<ellipse class="cls-5 eye eye-left" cx="245.15133" cy="134.57033" rx="5.31264" ry="8.61816" transform="translate(-33.47349 110.5587) rotate(-23.83807)" />
<ellipse class="cls-4 eye eye-right" cx="284.42686" cy="116.68559" rx="5.31264" ry="8.61816" transform="translate(-22.89477 124.9063) rotate(-23.83807)" />

We’ll configure the library and make it target the classes that we’ve added.

const optionsLeft = { power: 4, rotatable: false };
const watcherLeft = new WatchingYou(".eye-left", optionsLeft);
watcherLeft.start();

const optionsRight = { power: 3, rotatable: false };
const watcherRight = new WatchingYou(".eye-right", optionsRight);
watcherRight.start();

We also need to remember to apply the transform-box property, so our eyes move around the center.

.eye {
  transform-box: fill-box;
  transform-origin: center center;
}

Let’s check out what we’ve got. With just a few lines of code and a tiny JavaScript library to do the heavy lifting, we’ve made the SVG element respond to the mouse position. Now that’s amazing, isn’t it?

Bowtie and hat animation will be created in a very similar way. Let’s start with a hat and find it using the browser’s inspect element tool. The hat graphic consists of two path elements, so let’s group them.

<g class="hat">
  <path class="cls-6" d="..." />
  <path class="cls-9" d="..." />
</g>

We’ll apply the same transform-box property and add a hat--active class that will run the animation when applied.

.hat {
  transform-box: fill-box;
  transform-origin: center bottom;
  cursor: pointer;
}

.hat--active {
  animation: hatJump 1s cubic-bezier(0, 0.7, 0.5, 1.25);
}

@keyframes hatJump {
  0% {
    transform: rotateZ(0) translateY(0);
  }

  50% {
    transform: rotateZ(-10deg) translateY(-50%);
  }

  100% {
    transform: rotateZ(0) translateY(0);
  }
}

Finally, let’s set up a click event listener that applies an active class to the element and then removes it after the animation has finished running.

const hat = document.querySelector(".hat");

hat.addEventListener("click", function () {
  if (hat.classList.contains("hat--active")) {
    return;
  }
  // Add the active class.
  hat.classList.add("hat--active");

  // Remove the active class after 1.2s.
  setTimeout(function () {
    hat.classList.remove("hat--active");
  }, 1200);
});

We use the same trick with the bowtie element, only applying a different animation and class. Feel free to check out the CodePen demo for more details.

Let’s move on to the coffee machine. Notice we don’t have any SVG element acting as a coffee on our SVG, so we’ll need to add it ourselves. You should feel comfortable editing SVG markup and we don’t even have to break a sweat here. Let’s make it easy for ourselves and find and copy the coffee machine’s pipe rectangle, which is similar to the coffee stream shape we want to have. We just have to change the color to brown and slightly adjust the dimensions.

<!-- Pipe -->
<rect class="cls-12" x="137.81171" y="243.99883" width="6.21967" height="12.29272" transform="translate(281.84309 500.29037) rotate(-180)" />

<!-- Copied and adjusted Pipe rect to act as a coffee -->
<rect class="coffee" x="139" y="243.99883" width="4" height="12.29272" transform="translate(281.84309 500.29037) rotate(-180)" fill="brown" />

Like in the previous examples, let’s add active classes and their respective animation keyframes. We’ll compose the two animations and play around with duration and delay.

.lever, .coffee {
  transform-box: fill-box;
  transform-origin: center bottom;
}

.lever {   
  cursor: pointer; 
}

.lever--active {
  animation: leverPush 2.5s linear;
}

@keyframes leverPush {
  0% {
    transform: translateY(0);
  }
  8% {
    transform: translateY(50%);
  }
  90% {
    transform: translateY(50%);
  }
  100% {
    transform: translateY(0);
  }
}

.coffee--active {
  animation: coffeeStream 2.4s 0.1s ease-out forwards;
}

@keyframes coffeeStream {
  0% {
    transform: translateY(0);
  }
  5% {
    transform: translateY(50%);
  }
  95% {
    transform: translateY(50%);
  }
  100% {
    transform: translateY(150%);
  }
}

Let’s apply the active classes on click and remove them after the animation has finished running. And that’s it!

const lever = document.querySelector(".lever");
const coffee = document.querySelector(".coffee");

lever.addEventListener("click", function () {
  if (lever.classList.contains("lever--active")) {
    return;
  }

  lever.classList.add("lever--active");
  coffee.classList.add("coffee--active");

  setTimeout(function () {
    lever.classList.remove("lever--active");
    coffee.classList.remove("coffee--active")
  }, 2500);
});

Check out the complete example below, and, as always, feel free to play around with the animations and experiment with other elements, like the speech bubble or making the coffee machine’s lights blink while coffee is pouring out. Have fun!

See the Pen Smashing cat interaction [forked] by Adrian Bece.

Conclusion

I hope that this article encourages you to play around and make some wonderful SVG animations and interactions and integrate this workflow into your day-to-day projects. We’ve used only a handful of tricks and CSS properties to create a whole variety of nice effects on the fly. With some extra time, knowledge, and effort, you can create some truly amazing and interactive graphics.

Feel free to reach out on Twitter and share your work. Happy to hear your thoughts and see what you come up with!

References

Migration From jQuery to Next.js: A Guide

jQuery has served developers well for many years. However, libraries (like React) and Frameworks (like Next.js) are now bringing us more modern features to help with our code's performance and maintainability. This guide will show you how to rewrite your jQuery site using Next.js to take advantage of all these new features, such as client-side routing for smoother transitions and the ability to separate code into components to make it more reusable.

Getting started

The easiest way to get started with a Next.js is to run npx create-next-app. This will scaffold a project for you. However, to understand what this command does, we’ll create our application from scratch.

First, we’ll create our Next.js project using npm init. You can proceed with the default settings, as we will change them later. Then, we want to install React and Next.js using:

npm install react react-dom next

Next up, we can open the package.json file and replace the default scripts with:

"scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
}

This allows you to run npm run dev to start the development server; npm run build to build your application; and npm run start to start a server of that built application.

To add pages — like you would index.html with jQuery — create a directory named pages and create a file named index.jsx in it. Inside this file, place the following code:

export default function Index() {
  return <h1>Hello World</h1> ;
}

Now, by running npm run start and navigating to localhost:3000, you should see a h1 tag displayed. The name of this function isn’t important, so you can call it whatever you want. However, don’t use an anonymous arrow function, as this will prevent fast refresh from working.

CSS

In jQuery, you can specify CSS by page, importing different stylesheets for different pages. This is also possible in Next.js using the next/head component and a link tag the same way as jQuery. Anyhow, there are more performance-friendly ways to to this in Next.js.

Global Stylesheet

The first way is with a global stylesheet. To do so, we need to create a custom App by making the file _app.js inside the pages directory. The starting point for this file is as follows:

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

At the top of this file, you can add an import statement and import any CSS file you want. For example, if you created a separate folder at the root level called styles and put main.css in it, then you would add:

import "../styles/main.css"

Now, whatever you put inside this file will be applied throughout your application.

CSS Modules

The next option is CSS modules — which allows you to specify CSS anywhere in your application. They will create unique class names from the classes you provide, so you can use a same class name in multiple places in your application’s code.

Expanding the initial hello world example, you could create a file index.module.css in the same directory and then write the import:

import styles from "./index.module.css"

Afterwards, if you were to define a heading class in the CSS file, you could do the following:

export default function Index() {
  return <h1 className={styles.heading}>Hello World</h1> ;
}

and those styles will be applied only to that element.

Styled JSX

The final built-in option is styled JSX. This is most similar to including a <style> tag at the top of your page to define some styles. Simply add jsx to the <style> tag, and use a template string inside, like this:

<style jsx>{`
  .heading {
      font-weight: 700
  `}</style>

This option has the advantage of being changeable at runtime. For instance, if you wanted to supply the font weight in your component props, you could do:

<style jsx>{`
  .heading{
      font-weight: ${props.fontWeight}
  `}</style>

The one disadvantage of this method is that it introduces additional runtime JavaScript into your application, increasing the size by 12kb (3kb gzipped).

Events

In jQuery, you might have events set up to respond to DOM elements. To give you an idea, you might want to execute code when a p tag is clicked and do so like this:

$( "p" ).click(function() {
    console.log( "You clicked a paragraph!" );
});

Instead, React uses event handlers — which you might have seen in HTML — like onclick. Note that React uses camelCase instead, and so onclick should be referenced as onClick. Therefore, rewriting this small example into React would look like this:

export default function Index() {
  function clickParagraph(){
    console.log("You clicked a paragraph!");
  }
  return <p onClick={clickParagraph}>Hello World</p>;
}

Each method comes with its advantages and disadvantages. In jQuery, it is easy to have something happen for all paragraphs, whereas in React, you have to specify per paragraph. However, for larger codebases, having to specify makes it easy to see what will happen with the interaction with any element, where you may have forgotten about the jQuery function.

Effects

Effects are used in jQuery to show and hide content. You might have something like this already:

$( "p" ).hide();

In React, this behavior is implemented using conditional rendering. You can see this by combining it with the replacement for events we just saw:

import {useState} from "react"
export default function Index() {
  const [show, setShow] = useState(true);
  function clickButton(){
    setShow(false)
  }
  return (
    <div>
      <h1>Hello world</h1>
      {show && <button onClick={clickButton}>Click me</button>}
    </div>
  )
}

When you click this button, it will change the value of show to false and so, the statement won’t render anything. This can be expanded with the conditional operator to show one thing or another, depending on the value like this:

show ? <p>Show this if show is true</p> : <p>Show this if show is false</p>
Data Fetching

In jQuery, Ajax is used for external data fetching without reloading. In React, this can be done by using the useEffect hook. For this example, we’ll fetch the exchange rate from a public API when the page loads:

import { useState, useEffect } from "react";
export default function Index() {
  const [er, setEr] = useState(true);
  useEffect(async () => {
    const result = await fetch("https://api.exchangerate.host/latest");
    const exchangerate = await result.json();
    setEr(exchangerate.rates["GBP"]);
  }, []);
  return (
    <div>
      <h1>Hello world</h1>
      <p>Exchange rate: {er}</p>
    </div> 
  );
}

useEffect takes in a function and a dependency array. The function does the data fetching, using async as the fetch API asynchronously. We can then set any state we want in there, and it will be updated on the page. The dependency array determines which value changes will run the function. In this case, it’s set to an empty array which means that the function will only run when the page first loads.

Beyond this, Next.js also provides options for fetching data on the server or at build time. For build time data fetching, the function getStaticProps can be used. This function provides an improvement in performance as the data can be provided with the page — rather than waiting on an external service. To use it, create this function in a page as it doesn’t work in components.

export async function getStaticProps() {
  return {
    props: {},
  }
}

You can perform any data fetching you want before the return, and after that, pass the data through to the page under props — then, the data is provided to the page and can be accessed under the props.

By replacing the function name from getStaticProps to getServerSideProps, the function will be called on every request, giving you the flexibility to use Node.js functions if needed. It also allows you to make many data requests on the server and to process them to reduce the bandwidth used by the client.

You also have the option of a middle ground between the two called Incremental Static Regeneration. This option will generate a static page in the same way as getStaticProps, but it allows you to specify a revalidation period — which will regenerate the page when a request comes in at most as often as the period you specify. To do this, alongside props, you should also include a revalidate key with the time in seconds you want.

Objects into DOM elements

With jQuery, you have to be careful with which method you use for turning an object into DOM elements. The most common example of this is to create a list of items because, with jQuery, a loop over items would add each to the DOM one by one. With React, the virtual DOM is used to create diffs of the new state from the current one. This means that despite adding items in a loop, they are added to the real DOM as one operation.

This is done using the map function in JavaScript, where you can map each item to some JSX.

export default function Index() {
  const fruits = ["Apple", "Orange", "Pear"];
  return (
    <div>
      <h1>Hello world</h1>
      <ul>
        {fruits.map((fruit) => (
          <li key={fruit}>{fruit}</li>
        ))}
      </ul>
    </div>
  );
}

Notice that the element inside the map needs a key prop. This is used in the diffing process discussed above, making it easy for React to distinguish between each element, so each of these should be unique.

Deffereds

The use of deferreds in jQuery can be replaced with the native JavaScript promise functionality. The syntax for deffereds was designed to mirror the functionality of promises, so the syntax should be familiar and not require too much alteration. One example of where deffereds might be used is in data fetching. If you do this with the fetch method in JavaScript, then you can add a .then to the end of the fetch as it returns a promise. This code will only run when the fetch is completed, and so the data (or an error) will be present. You can see this functionality in use here:

fetch("example.com")
.then((response) => {
  console.log(response)
})
.catch((error) => {
console.error(error)
})

This will fetch example.com and log the fetched response unless an error occurs — in this case it will be logged as an error.

In addition to this syntax, the newer async/await syntax can also be used. These require a function defined as a`sync`, in the same way as you might export a function. You can declare it like so:

async function myFunction(){
  return
}

Inside this function, you can call further async functions by placing await in front of them, for example:

async function myFunction(){
  const data = await fetch("example.com")
  return data
}

This code will return a promise that will resolve when the data is fetched, so it needs to be called inside an asynchronous function to await the result. However, in order to also catch errors, you will need to write a conditional to check the response status — if data.ok isn’t true, an error should be thrown. Then, you can wrap these away statements in a try catch block, rather than using .catch. You can read more about these methods in this article.

Improvements

Routing

Next.js uses file system routing, which is very similar to using different .html pages in a traditional website. However, this system also offers features beyond that, providing dynamic routes and allowing one page to be accessed under a range of urls.

For example, if you have a blog, you might keep all your files under /blog/*, creating a file [slug].jsx inside the blog folder — which will allow that content to be served for all pages under blog. Then, you can use the router in Next.js to find which route has been navigated to, like so:

const router = useRouter()
const { slug } = router.query

API routes

API routes allow you to also write your backend inside your Next.js application. To use these routes, create an api folder in your pages directory — now, any files created inside it will run on the server rather than the client, as with the rest of the pages.

To get started with these, you need to export a default function from the file, and this can take two parameters. The first will be the incoming request, and the second will let you create the response. A basic API route can be written like this:

export default function handler(request, response) {
  response.status(200).json({ magazine: 'Smashing' })
}
Limitations

jQuery UI

You may use jQuery UI in your application for user interface, but React doesn’t provide an official UI library like this. Nevertheless, a range of alternatives has been produced. Two of the most popular are Reach UI and React Aria. Both of these alternatives focus very strongly on Accessibility, ensuring that the project you create is usable by a bigger range of users.

Animation

While you can use conditional rendering instead of effects, this doesn’t provide all the same functionality, as you can’t do things such as fading content out. One library that helps to provide this functionality is React Transition Group — which allows you to define entering and exiting transitions.

Conclusion

Moving from jQuery to Next.js is a large undertaking, especially for big code bases. However, this migration allows you to use newer concepts (such as data fetching at build time) and sets you up to have simple migration paths to new versions of React and Next.js — along with the features they bring.

React can help you better organize your code (which is particularly important for large codebases) and brings a substantial performance improvement through the use of a virtual DOM. Overall, I believe that migrating from jQuery to Next.js is worth the effort, and I hope that if you decide to migrate, you enjoy all the features React and Next.js have to offer.

Further Reading on Smashing Magazine

Building Interoperable Web Components That Even Work With React

Those of us who’ve been web developers more than a few years have probably written code using more than one JavaScript framework. With all the choices out there — React, Svelte, Vue, Angular, Solid — it’s all but inevitable. One of the more frustrating things we have to deal with when working across frameworks is re-creating all those low-level UI components: buttons, tabs, dropdowns, etc. What’s particularly frustrating is that we’ll typically have them defined in one framework, say React, but then need to rewrite them if we want to build something in Svelte. Or Vue. Or Solid. And so on.

Wouldn’t it be better if we could define these low-level UI components once, in a framework-agnostic way, and then re-use them between frameworks? Of course it would! And we can; web components are the way. This post will show you how.

As of now, the SSR story for web components is a bit lacking. Declarative shadow DOM (DSD) is how a web component is server-side rendered, but, as of this writing, it’s not integrated with your favorite application frameworks like Next, Remix or SvelteKit. If that’s a requirement for you, be sure to check the latest status of DSD. But otherwise, if SSR isn’t something you’re using, read on.

First, some context

Web Components are essentially HTML elements that you define yourself, like <yummy-pizza> or whatever, from the ground up. They’re covered all over here at CSS-Tricks (including an extensive series by Caleb Williams and one by John Rhea) but we’ll briefly walk through the process. Essentially, you define a JavaScript class, inherit it from HTMLElement, and then define whatever properties, attributes and styles the web component has and, of course, the markup it will ultimately render to your users.

Being able to define custom HTML elements that aren’t bound to any particular component is exciting. But this freedom is also a limitation. Existing independently of any JavaScript framework means you can’t really interact with those JavaScript frameworks. Think of a React component which fetches some data and then renders some other React component, passing along the data. This wouldn’t really work as a web component, since a web component doesn’t know how to render a React component.

Web components particularly excel as leaf components. Leaf components are the last thing to be rendered in a component tree. These are the components which receive some props, and render some UI. These are not the components sitting in the middle of your component tree, passing data along, setting context, etc. — just pure pieces of UI that will look the same, no matter which JavaScript framework is powering the rest of the app.

The web component we’re building

Rather than build something boring (and common), like a button, let’s build something a little bit different. In my last post we looked at using blurry image previews to prevent content reflow, and provide a decent UI for users while our images load. We looked at base64 encoding a blurry, degraded versions of our images, and showing that in our UI while the real image loaded. We also looked at generating incredibly compact, blurry previews using a tool called Blurhash.

That post showed you how to generate those previews and use them in a React project. This post will show you how to use those previews from a web component so they can be used by any JavaScript framework.

But we need to walk before we can run, so we’ll walk through something trivial and silly first to see exactly how web components work.

Everything in this post will build vanilla web components without any tooling. That means the code will have a bit of boilerplate, but should be relatively easy to follow. Tools like Lit or Stencil are designed for building web components and can be used to remove much of this boilerplate. I urge you to check them out! But for this post, I’ll prefer a little more boilerplate in exchange for not having to introduce and teach another dependency.

A simple counter component

Let’s build the classic “Hello World” of JavaScript components: a counter. We’ll render a value, and a button that increments that value. Simple and boring, but it’ll let us look at the simplest possible web component.

In order to build a web component, the first step is to make a JavaScript class, which inherits from HTMLElement:

class Counter extends HTMLElement {}

The last step is to register the web component, but only if we haven’t registered it already:

if (!customElements.get("counter-wc")) {
  customElements.define("counter-wc", Counter);
}

And, of course, render it:

<counter-wc></counter-wc>

And everything in between is us making the web component do whatever we want it to. One common lifecycle method is connectedCallback, which fires when our web component is added to the DOM. We could use that method to render whatever content we’d like. Remember, this is a JS class inheriting from HTMLElement, which means our this value is the web component element itself, with all the normal DOM manipulation methods you already know and love.

At it’s most simple, we could do this:

class Counter extends HTMLElement {
  connectedCallback() {
    this.innerHTML = "<div style='color: green'>Hey</div>";
  }
}

if (!customElements.get("counter-wc")) {
  customElements.define("counter-wc", Counter);
}

…which will work just fine.

The word "hey" in green.

Adding real content

Let’s add some useful, interactive content. We need a <span> to hold the current number value and a <button> to increment the counter. For now, we’ll create this content in our constructor and append it when the web component is actually in the DOM:

constructor() {
  super();
  const container = document.createElement('div');

  this.valSpan = document.createElement('span');

  const increment = document.createElement('button');
  increment.innerText = 'Increment';
  increment.addEventListener('click', () => {
    this.#value = this.#currentValue + 1;
  });

  container.appendChild(this.valSpan);
  container.appendChild(document.createElement('br'));
  container.appendChild(increment);

  this.container = container;
}

connectedCallback() {
  this.appendChild(this.container);
  this.update();
}

If you’re really grossed out by the manual DOM creation, remember you can set innerHTML, or even create a template element once as a static property of your web component class, clone it, and insert the contents for new web component instances. There’s probably some other options I’m not thinking of, or you can always use a web component framework like Lit or Stencil. But for this post, we’ll continue to keep it simple.

Moving on, we need a settable JavaScript class property named value

#currentValue = 0;

set #value(val) {
  this.#currentValue = val;
  this.update();
}

It’s just a standard class property with a setter, along with a second property to hold the value. One fun twist is that I’m using the private JavaScript class property syntax for these values. That means nobody outside our web component can ever touch these values. This is standard JavaScript that’s supported in all modern browsers, so don’t be afraid to use it.

Or feel free to call it _value if you prefer. And, lastly, our update method:

update() {
  this.valSpan.innerText = this.#currentValue;
}

It works!

The counter web component.

Obviously this is not code you’d want to maintain at scale. Here’s a full working example if you’d like a closer look. As I’ve said, tools like Lit and Stencil are designed to make this simpler.

Adding some more functionality

This post is not a deep dive into web components. We won’t cover all the APIs and lifecycles; we won’t even cover shadow roots or slots. There’s endless content on those topics. My goal here is to provide a decent enough introduction to spark some interest, along with some useful guidance on actually using web components with the popular JavaScript frameworks you already know and love.

To that end, let’s enhance our counter web component a bit. Let’s have it accept a color attribute, to control the color of the value that’s displayed. And let’s also have it accept an increment property, so consumers of this web component can have it increment by 2, 3, 4 at a time. And to drive these state changes, let’s use our new counter in a Svelte sandbox — we’ll get to React in a bit.

We’ll start with the same web component as before and add a color attribute. To configure our web component to accept and respond to an attribute, we add a static observedAttributes property that returns the attributes that our web component listens for.

static observedAttributes = ["color"];

With that in place, we can add a attributeChangedCallback lifecycle method, which will run whenever any of the attributes listed in observedAttributes are set, or updated.

attributeChangedCallback(name, oldValue, newValue) {
  if (name === "color") {
    this.update();
  }
}

Now we update our update method to actually use it:

update() {
  this.valSpan.innerText = this._currentValue;
  this.valSpan.style.color = this.getAttribute("color") || "black";
}

Lastly, let’s add our increment property:

increment = 1;

Simple and humble.

Using the counter component in Svelte

Let’s use what we just made. We’ll go into our Svelte app component and add something like this:

<script>
  let color = "red";
</script>

<style>
  main {
    text-align: center;
  }
</style>

<main>
  <select bind:value={color}>
    <option value="red">Red</option>
    <option value="green">Green</option>
    <option value="blue">Blue</option>
  </select>

  <counter-wc color={color}></counter-wc>
</main>

And it works! Our counter renders, increments, and the dropdown updates the color. As you can see, we render the color attribute in our Svelte template and, when the value changes, Svelte handles the legwork of calling setAttribute on our underlying web component instance. There’s nothing special here: this is the same thing it already does for the attributes of any HTML element.

Things get a little bit interesting with the increment prop. This is not an attribute on our web component; it’s a prop on the web component’s class. That means it needs to be set on the web component’s instance. Bear with me, as things will wind up much simpler in a bit.

First, we’ll add some variables to our Svelte component:

let increment = 1;
let wcInstance;

Our powerhouse of a counter component will let you increment by 1, or by 2:

<button on:click={() => increment = 1}>Increment 1</button>
<button on:click={() => increment = 2}>Increment 2</button>

But, in theory, we need to get the actual instance of our web component. This is the same thing we always do anytime we add a ref with React. With Svelte, it’s a simple bind:this directive:

<counter-wc bind:this={wcInstance} color={color}></counter-wc>

Now, in our Svelte template, we listen for changes to our component’s increment variable and set the underlying web component property.

$: {
  if (wcInstance) {
    wcInstance.increment = increment;
  }
}

You can test it out over at this live demo.

We obviously don’t want to do this for every web component or prop we need to manage. Wouldn’t it be nice if we could just set increment right on our web component, in markup, like we normally do for component props, and have it, you know, just work? In other words, it’d be nice if we could delete all usages of wcInstance and use this simpler code instead:

<counter-wc increment={increment} color={color}></counter-wc>

It turns out we can. This code works; Svelte handles all that legwork for us. Check it out in this demo. This is standard behavior for pretty much all JavaScript frameworks.

So why did I show you the manual way of setting the web component’s prop? Two reasons: it’s useful to understand how these things work and, a moment ago, I said this works for “pretty much” all JavaScript frameworks. But there’s one framework which, maddeningly, does not support web component prop setting like we just saw.

React is a different beast

React. The most popular JavaScript framework on the planet does not support basic interop with web components. This is a well-known problem that’s unique to React. Interestingly, this is actually fixed in React’s experimental branch, but for some reason wasn’t merged into version 18. That said, we can still track the progress of it. And you can try this yourself with a live demo.

The solution, of course, is to use a ref, grab the web component instance, and manually set increment when that value changes. It looks like this:

import React, { useState, useRef, useEffect } from 'react';
import './counter-wc';

export default function App() {
  const [increment, setIncrement] = useState(1);
  const [color, setColor] = useState('red');
  const wcRef = useRef(null);

  useEffect(() => {
    wcRef.current.increment = increment;
  }, [increment]);

  return (
    <div>
      <div className="increment-container">
        <button onClick={() => setIncrement(1)}>Increment by 1</button>
        <button onClick={() => setIncrement(2)}>Increment by 2</button>
      </div>

      <select value={color} onChange={(e) => setColor(e.target.value)}>
        <option value="red">Red</option>
        <option value="green">Green</option>
        <option value="blue">Blue</option>
      </select>

      <counter-wc ref={wcRef} increment={increment} color={color}></counter-wc>
    </div>
  );
}

As we discussed, coding this up manually for every web component property is simply not scalable. But all is not lost because we have a couple of options.

Option 1: Use attributes everywhere

We have attributes. If you clicked the React demo above, the increment prop wasn’t working, but the color correctly changed. Can’t we code everything with attributes? Sadly, no. Attribute values can only be strings. That’s good enough here, and we’d be able to get somewhat far with this approach. Numbers like increment can be converted to and from strings. We could even JSON stringify/parse objects. But eventually we’ll need to pass a function into a web component, and at that point we’d be out of options.

Option 2: Wrap it

There’s an old saying that you can solve any problem in computer science by adding a level of indirection (except the problem of too many levels of indirection). The code to set these props is pretty predictable and simple. What if we hide it in a library? The smart folks behind Lit have one solution. This library creates a new React component for you after you give it a web component, and list out the properties it needs. While clever, I’m not a fan of this approach.

Rather than have a one-to-one mapping of web components to manually-created React components, what I prefer is just one React component that we pass our web component tag name to (counter-wc in our case) — along with all the attributes and properties — and for this component to render our web component, add the ref, then figure out what is a prop and what is an attribute. That’s the ideal solution in my opinion. I don’t know of a library that does this, but it should be straightforward to create. Let’s give it a shot!

This is the usage we’re looking for:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

wcTag is the web component tag name; the rest are the properties and attributes we want passed along.

Here’s what my implementation looks like:

import React, { createElement, useRef, useLayoutEffect, memo } from 'react';

const _WcWrapper = (props) => {
  const { wcTag, children, ...restProps } = props;
  const wcRef = useRef(null);

  useLayoutEffect(() => {
    const wc = wcRef.current;

    for (const [key, value] of Object.entries(restProps)) {
      if (key in wc) {
        if (wc[key] !== value) {
          wc[key] = value;
        }
      } else {
        if (wc.getAttribute(key) !== value) {
          wc.setAttribute(key, value);
        }
      }
    }
  });

  return createElement(wcTag, { ref: wcRef });
};

export const WcWrapper = memo(_WcWrapper);

The most interesting line is at the end:

return createElement(wcTag, { ref: wcRef });

This is how we create an element in React with a dynamic name. In fact, this is what React normally transpiles JSX into. All our divs are converted to createElement("div") calls. We don’t normally need to call this API directly but it’s there when we need it.

Beyond that, we want to run a layout effect and loop through every prop that we’ve passed to our component. We loop through all of them and check to see if it’s a property with an in check that checks the web component instance object as well as its prototype chain, which will catch any getters/setters that wind up on the class prototype. If no such property exists, it’s assumed to be an attribute. In either case, we only set it if the value has actually changed.

If you’re wondering why we use useLayoutEffect instead of useEffect, it’s because we want to immediately run these updates before our content is rendered. Also, note that we have no dependency array to our useLayoutEffect; this means we want to run this update on every render. This can be risky since React tends to re-render a lot. I ameliorate this by wrapping the whole thing in React.memo. This is essentially the modern version of React.PureComponent, which means the component will only re-render if any of its actual props have changed — and it checks whether that’s happened via a simple equality check.

The only risk here is that if you’re passing an object prop that you’re mutating directly without re-assigning, then you won’t see the updates. But this is highly discouraged, especially in the React community, so I wouldn’t worry about it.

Before moving on, I’d like to call out one last thing. You might not be happy with how the usage looks. Again, this component is used like this:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

Specifically, you might not like passing the web component tag name to the <WcWrapper> component and prefer instead the @lit-labs/react package above, which creates a new individual React component for each web component. That’s totally fair and I’d encourage you to use whatever you’re most comfortable with. But for me, one advantage with this approach is that it’s easy to delete. If by some miracle React merges proper web component handling from their experimental branch into main tomorrow, you’d be able to change the above code from this:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

…to this:

<counter-wc ref={wcRef} increment={increment} color={color} />

You could probably even write a single codemod to do that everywhere, and then delete <WcWrapper> altogether. Actually, scratch that: a global search and replace with a RegEx would probably work.

The implementation

I know, it seems like it took a journey to get here. If you recall, our original goal was to take the image preview code we looked at in my last post, and move it to a web component so it can be used in any JavaScript framework. React’s lack of proper interop added a lot of detail to the mix. But now that we have a decent handle on how to create a web component, and use it, the implementation will almost be anti-climactic.

I’ll drop the entire web component here and call out some of the interesting bits. If you’d like to see it in action, here’s a working demo. It’ll switch between my three favorite books on my three favorite programming languages. The URL for each book will be unique each time, so you can see the preview, though you’ll likely want to throttle things in your DevTools Network tab to really see things taking place.

View entire code
class BookCover extends HTMLElement {
  static observedAttributes = ['url'];

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'url') {
      this.createMainImage(newValue);
    }
  }

  set preview(val) {
    this.previewEl = this.createPreview(val);
    this.render();
  }

  createPreview(val) {
    if (typeof val === 'string') {
      return base64Preview(val);
    } else {
      return blurHashPreview(val);
    }
  }

  createMainImage(url) {
    this.loaded = false;
    const img = document.createElement('img');
    img.alt = 'Book cover';
    img.addEventListener('load', () =&gt; {
      if (img === this.imageEl) {
        this.loaded = true;
        this.render();
      }
    });
    img.src = url;
    this.imageEl = img;
  }

  connectedCallback() {
    this.render();
  }

  render() {
    const elementMaybe = this.loaded ? this.imageEl : this.previewEl;
    syncSingleChild(this, elementMaybe);
  }
}

First, we register the attribute we’re interested in and react when it changes:

static observedAttributes = ['url'];

attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'url') {
    this.createMainImage(newValue);
  }
}

This causes our image component to be created, which will show only when loaded:

createMainImage(url) {
  this.loaded = false;
  const img = document.createElement('img');
  img.alt = 'Book cover';
  img.addEventListener('load', () => {
    if (img === this.imageEl) {
      this.loaded = true;
      this.render();
    }
  });
  img.src = url;
  this.imageEl = img;
}

Next we have our preview property, which can either be our base64 preview string, or our blurhash packet:

set preview(val) {
  this.previewEl = this.createPreview(val);
  this.render();
}

createPreview(val) {
  if (typeof val === 'string') {
    return base64Preview(val);
  } else {
    return blurHashPreview(val);
  }
}

This defers to whichever helper function we need:

function base64Preview(val) {
  const img = document.createElement('img');
  img.src = val;
  return img;
}

function blurHashPreview(preview) {
  const canvasEl = document.createElement('canvas');
  const { w: width, h: height } = preview;

  canvasEl.width = width;
  canvasEl.height = height;

  const pixels = decode(preview.blurhash, width, height);
  const ctx = canvasEl.getContext('2d');
  const imageData = ctx.createImageData(width, height);
  imageData.data.set(pixels);
  ctx.putImageData(imageData, 0, 0);

  return canvasEl;
}

And, lastly, our render method:

connectedCallback() {
  this.render();
}

render() {
  const elementMaybe = this.loaded ? this.imageEl : this.previewEl;
  syncSingleChild(this, elementMaybe);
}

And a few helpers methods to tie everything together:

export function syncSingleChild(container, child) {
  const currentChild = container.firstElementChild;
  if (currentChild !== child) {
    clearContainer(container);
    if (child) {
      container.appendChild(child);
    }
  }
}

export function clearContainer(el) {
  let child;

  while ((child = el.firstElementChild)) {
    el.removeChild(child);
  }
}

It’s a little bit more boilerplate than we’d need if we build this in a framework, but the upside is that we can re-use this in any framework we’d like — although React will need a wrapper for now, as we discussed.

Odds and ends

I’ve already mentioned Lit’s React wrapper. But if you find yourself using Stencil, it actually supports a separate output pipeline just for React. And the good folks at Microsoft have also created something similar to Lit’s wrapper, attached to the Fast web component library.

As I mentioned, all frameworks not named React will handle setting web component properties for you. Just note that some have some special flavors of syntax. For example, with Solid.js, <your-wc value={12}> always assumes that value is a property, which you can override with an attr prefix, like <your-wc attr:value={12}>.

Wrapping up

Web components are an interesting, often underused part of the web development landscape. They can help reduce your dependence on any single JavaScript framework by managing your UI, or “leaf” components. While creating these as web components — as opposed to Svelte or React components — won’t be as ergonomic, the upside is that they’ll be widely reusable.


Building Interoperable Web Components That Even Work With React originally published on CSS-Tricks. You should get the newsletter.

9 New React and JavaScript Links for February 2022

Every now and then, I find that I’ve accumulated a bunch of links about various things I find interesting. Like React and JavaScript! Here’s a list of nine links to other articles about them that I’ve been saving up and think are worth sharing.

React and JavaScript code snippets with colorful hand-marked scribbles of notes.
Source: “Good advice on JSX conditionals” by Vladimir Klepov
  • Seed Funding for Remix
    Remix went open source after taking funding which seems like a solid move. It’s a for-now-React-only framework, so I think it’s fair that everyone asks how does it compare to Next.js. Which they answered. Probably worth noting again for us CSS folks, Kent mentioned: “Because Remix allows me to easily control which of my CSS files is on the page at any given time, I don’t have all the problems that triggered the JavaScript community to invent workarounds like CSS-in-JS.”
  • React Router v6
    Speaking of that gang, they released React Router v6, which looks like a positive move — all hooks based, 50% smaller than v5 — but is yet another major version with API changes. React Router has a history of API changes like this and they trigger plenty of grumbling in the community. There is plenty of that again.
  • React Aria
    “A library of React Hooks that provides accessible UI primitives for your design system” from… Adobe. Interesting. Looks like some pretty hard problems being solved here, like FocusScope (“When the contain prop is set, focus is contained within the scope.”) and interesting color inputs, like useColorField, useColorSlider, and useColorWheel. There are 59 hooks in all, ranging from interactions and forms to overlays and internationalization, with plenty of others in between.
  • Front End Tables: Sorting, Filtering, and Pagination
    Tania Rascia: “One thing I’ve had to do at every job I’ve had is implement a table on the front end of an application that has sorting, filtering, and pagination.” No shame in reaching for a big library with all these features, but sometimes it’s best to DIY.
  • Good advice on JSX conditionals
    Vladimir Klepov covers the (weirdly) many ways fairly simple conditionals can go wrong, like the number 0 leaking into your markup, and how to manage update versus remount in conditionals.
  • useProseMirror
    I’ve found ProseMirror to be a pretty nice rich text editor in the past. The library itself isn’t actually in React, so I think it’s a smart call here to make a modern React wrapper for it.
  • Spead up sluggish inputs with useDeferredValue
    You can introduce gnarly input delay the more work that an onChange function has to do on a text input. useDeferredValue gives us a way to separate high priority updates from low priority updates for cases like this.”
  • 🎥 A Cartoon Intro to WebAssembly
    If you don’t have a good understanding of what WebAssembly is, then Lin Clark will get you there in this video from JSConf EU 2017. So, no, not a new link or anything, but it’s new to me!
  • 🎥 Turborepo Demo and Walkthrough
    Vercel bought Turborepo. Turborepo is specifically focused on making monorepos better. As someone who’s main codebase is a monorepo with Lerna and Yarn Workspaces such that we can have multiple different sites all share things like a design system, this is right up our alley. This video is with the Turborepo creator Jared Palmer and Lee Robinson, head of developer relations at Vercel. In this video, you get to see it all work.


9 New React and JavaScript Links for February 2022 originally published on CSS-Tricks. You should get the newsletter.

Building A Web Code Editor

An online web code editor is most useful when you do not have the opportunity to use a code editor application, or when you want to quickly try out something on the web with your computer or even your mobile phone. This is also an interesting project to work on because having the knowledge of how to build a code editor will give you ideas on how to approach other projects that require you to integrate a code editor to show some functionality.

Here are a few React concepts you’ll need to know in order to follow along in this article:

  • Hooks,
  • Component structure,
  • Functional components,
  • Props.
Using CodeMirror

We will be using a library named CodeMirror to build our editor. CodeMirror is a versatile text editor implemented in JavaScript for the browser. It is especially for editing code and comes with a number of language modes and add-ons for more advanced editing functionality.

A rich programming API and a CSS theming system are available for customizing CodeMirror to fit your application and extending it with new functionality. It gives us the functionality to create a rich code editor that runs on the web and shows us the result of our code in real time.

In the next section, we will set up our new React project and install the libraries we need to build our web app.

Creating A New React Project

Let’s start by creating a new React project. In your commandline interface, navigate to the directory in which you want to create your project, and let’s create a React application and name it code_editor:

npx create-react-app code_editor

Having created our new React application, let’s navigate to that project’s directory in the commandline interface:

cd code_editor

There are two libraries we need to install here: codemirror and react-codemirror2.

npm install codemirror react-codemirror2

Having installed the libraries we need for this project, let’s create our tabs and enable tab switching between the three tabs that will appear in our editor (for HTML, CSS, and JavaScript).

Button Component

Instead of creating individual buttons, let’s make the button a component that is reusable. In our project, the button would have three instances, according to the three tabs we need.

Create a folder named components in the src folder. In this new components folder, create a JSX file named Button.jsx.

Here is all of the code needed in the Button component:

import React from 'react'
const Button = ({title, onClick}) => {
  return (
    <div>
      <button
        style={{
          maxWidth: "140px",
          minWidth: "80px",
          height: "30px",
          marginRight: "5px"
        }}
        onClick={onClick}
      >
        {title}
      </button>
    </div>
  )
}
export default Button

Here is a full explanation of what we did above:

  • We created a functional component named Button, which we then exported.
  • We destructured title and onClick from the props coming into the component. Here, title would be a string of text, and onClick would be a function that gets called when a button is clicked.
  • Next, we used the button element to declare our button, and used the style attributes to style our button to look presentable.
  • We added the onClick attribute and passed our destructured onClick function props to it.
  • The last thing you’ll notice we did in this component is pass in {title} as the content of the button tag. This allows us to display the title dynamically, based on what prop is being passed to the instance of the button component when it is called.

Now that we have created a reusable button component, let’s move on and bring our component into App.js. Go to App.js and import the newly created button component:

import Button from './components/Button';

To track which tab or editor is open, we need a declare state to hold the value of the editor that is open. Using the useState React hook, we’ll set up the state that will store the name of the editor tab that is currently open when that tab’s button is clicked.

Here is how we do that:

import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';

function App() {
  const [openedEditor, setOpenedEditor] = useState('html');
  return (
    <div className="App">
    </div>
  );
}
export default App;

Here, we declared our state. It takes the name of the editor that is currently open. Because the value html is passed as the state’s default value, the HTML editor would be the tab open by default.

Let’s move on and write the function that will use setOpenedEditor to change the value of the state when a tab button is clicked.

Note: Two tabs may not be open at the same time, so we’ll have to consider that when writing our function.

Here is what our function, named onTabClick, looks like:

import React, { useState } from 'react';
import './App.css';
import Button from './components/Button';

function App() {
  ...

  const onTabClick = (editorName) => {
    setOpenedEditor(editorName);
  };

  return (
    <div className="App">
    </div>
  );
}
export default App;

Here, we passed a single function argument, which is the name of the tab currently selected. This argument would be supplied anywhere the function is called, and the relevant name of that tab would be passed in.

Let’s create three instances of our Button for the three tabs we need:

<div className="App">
      <p>Welcome to the editor!</p>
      <div className="tab-button-container">
        <Button title="HTML" onClick={() => {
          onTabClick('html')
        }} />
        <Button title="CSS" onClick={() => {
          onTabClick('css')
        }} />
        <Button title="JavaScript" onClick={() => {
          onTabClick('js')
        }} />
      </div>
    </div>

Here is what we did:

  • We started by adding a p tag, basically just to give some context to what our application is about.
  • We used a div tag to wrap our tab buttons. The div tag carries a className that we will use to style the buttons into a grid display in the CSS file later in this tutorial.
  • Next, we declared three instances of the Button component. If you recall, the Button component takes two props, title and onClick. In every instance of the Button component, these two props are provided.
  • The title prop takes the title of the tab.
  • The onClick prop takes a function, onTabClick, which we just created and which takes a single argument: the name of the tab selected.

Based on the tab currently selected, we would use the JavaScript ternary operator to display the tab conditionally. This means that if the value of the openedEditor state is set to html (i.e. setOpenedEditor('html')), then the tab for the HTML section would become the currently visible tab. You’ll understand this better as we do it below:

...
return (
    <div className="App">
      ...
      <div className="editor-container">
        {
          openedEditor === 'html' ? (
            <p>The html editor is open</p>
          ) : openedEditor === 'css' ? (
            <p>The CSS editor is open!!!!!!</p>
          ) : (
            <p>the JavaScript editor is open</p>
          )
        }
      </div>
    </div>
  );
...

Let’s go over the code above in plain English. If the value of openedEditor is html, then display the HTML section. Otherwise, if the value of openedEditor is css, then display the CSS section. Otherwise, if the value is neither html nor css, then that means the value must be js, because we have only three possible values for the openedEditor state; so, then we’d display the tab for JavaScript.

We used paragraph tags (p) for the different sections in the ternary operator conditions. As we proceed, we will create the editor components and replace the p tags with the editor components themselves.

We have come so far already! When a button is clicked, it fires up the action that sets the tab it represents to true, making that tab visible. Here’s what our app currently looks like:

A GIF showing the tab toggle we currently have. (Large preview)

Let’s add a little CSS to the div container holding the buttons. We want the buttons to be displayed in a grid, instead of stacked vertically like in the image above. Go to your App.css file and add the following code:

.tab-button-container{
  display: flex;
}

Recall that we added className="tab-button-container" as an attribute in the div tag holding the three-tab buttons. Here, we styled that container, using CSS to set its display to flex. This is the result:

(Large preview)

Be proud of how much you’ve done to get to this point. In the next section, we will create our editors, replacing the p tags with them.

Creating the Editors

Because we have already installed the libraries we are going to be working on within our CodeMirror editor, let’s go ahead and create our Editor.jsx file in the components folder.

components > Editor.jsx

Having created our new file, let’s write some initial code in it:

import React, { useState } from 'react';
import 'codemirror/lib/codemirror.css';
import { Controlled as ControlledEditorComponent } from 'react-codemirror2';


const Editor = ({ language, value, setEditorState }) => {
  return (
    <div className="editor-container">
    </div>
  )
}
export default Editor

Here's what we did:

  • We imported React alongside the useState hook because we are going to need it.
  • We imported the CodeMirror CSS file (which comes from the CodeMirror library that we installed, so you don’t have to install it in any special way).
  • We imported Controlled from react-codemirror2, renaming it to ControlledEditorComponent to make it clearer. We will be using this shortly.
  • Then, we declared our Editor functional component, and we have a return statement with an empty div, with a className in the return statement for now.

In our functional component, we destructured some values from the props, including language, value, and setEditorState. These three props would be supplied in any instance of the editor when it is called in App.js.

Let’s use ControlledEditorComponent to write the code for our editor. Here’s what we’ll do:

import React, { useState } from 'react';
import 'codemirror/lib/codemirror.css';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/css/css';
import { Controlled as ControlledEditorComponent } from 'react-codemirror2';


const Editor = ({ language, value, setEditorState }) => {
  return (
    <div className="editor-container">
      <ControlledEditorComponent
        onBeforeChange={handleChange}
        value= {value}
        className="code-mirror-wrapper"
        options={{
          lineWrapping: true,
          lint: true,
          mode: language,
          lineNumbers: true,
        }}
      />
    </div>
  )
}
export default Editor

Let’s walk through what we did here, explaining some CodeMirror terms.

The CodeMirror modes specify which language an editor is meant for. We imported three modes because we have three editors for this project:

  1. XML: This mode is for HTML. It uses the term XML.
  2. JavaScript: This (codemirror/mode/javascript/javascript) brings in JavaScript mode.
  3. CSS: This (codemirror/mode/css/css) brings in CSS mode.

Note: Because the editor is built as a component that is reusable, we cannot put a direct mode in the editor. So, we supply the mode through the language prop that we destructured. But this doesn’t change the fact that the modes need to be imported in order to work.

Next, let’s discuss the things in ControlledEditorComponent:

  • onBeforeChange
    This is called anytime you write to or remove from the editor. Think of this like the onChange handler you would normally have in an input field to track changes. Using this, we will be able to get the value of our editor anytime there's a new change and save it to our editor’s state. We will write the {handleChange} function as we proceed.
  • value = {value}
    This is just the content of the editor at any given time. We passed a destructured prop named value to this attribute. The value props is the state holding the value of that editor. This would be supplied from the editor’s instance.
  • className="code-mirror-wrapper"
    This class name is not a style we make ourselves. It is supplied from CodeMirror’s CSS file, which we imported above.
  • options
    This is an object that takes the different functionality we want our editor to have. There are many amazing options in CodeMirror. Let’s look at the ones we used here:
    • lineWrapping: true
      This means that code should wrap to the next line when the line is full.
    • lint: true
      This allows linting.
    • mode: language
      This mode, as discussed above, takes the language that the editor is going to be used for. The language has already been imported above, but the editor is going to apply a language based on the language value supplied to the editor via the prop.
    • lineNumbers: true
      This specifies that the editor should have line numbers for each line.

Next, we can write the handleChange function for the onBeforeChange handler:

const handleChange = (editor, data, value) => {
    setEditorState(value);
}

The onBeforeChange handler gives us access to three things: editor, data, value.

We only need the value because it is what we want to pass in our setEditorState prop. The setEditorState prop represents the set value for each state that we declared in App.js, holding the value for each editor. As we move on, we will look at how to pass this as a prop to the Editor component.

Next, we’ll add a dropdown that allows us to select different themes for the editor. So, let’s look at themes in CodeMirror.

CodeMirror Themes

CodeMirror has multiple themes we can select from. Visit the official website to see demos of the different themes available. Let’s make a dropdown with different themes that the user can choose from in our editor. For this tutorial, we’ll be adding five themes, but you can add as many as you like.

First, let’s import our themes in the Editor.js component:

import 'codemirror/theme/dracula.css';
import 'codemirror/theme/material.css';
import 'codemirror/theme/mdn-like.css';
import 'codemirror/theme/the-matrix.css';
import 'codemirror/theme/night.css';

Next, create an array of all of the themes we have imported:

const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']

Let’s declare a useState hook to hold the value of the selected theme, and set the default theme as dracula:

const [theme, setTheme] = useState("dracula")

Let’s create the dropdown:

...
return (
    <div className="editor-container">

      <div style={{marginBottom: "10px"}}>
        <label for="cars">Choose a theme: </label>
        <select name="theme" onChange={(el) => {
          setTheme(el.target.value)
        }}>
          {
            themeArray.map( theme => (
              <option value={theme}>{theme}</option>
            ))
          }
        </select>
      </div>
    // the rest of the code comes below...
    </div>
  )
...

In the code above, we used the label HTML tag to add a label to our dropdown, and then added the select HTML tag to create our dropdown. The option tag in the select element defines the options available in the dropdown.

Because we needed to fill the dropdown with the theme names in the themeArray that we created, we used the .map array method to map themeArray and display the names individually using the option tag.

Hold on — we’re not done explaining the code above. In the opening select tag, we passed the onChange attribute to track and update the theme state whenever a new value is selected in the dropdown. Whenever a new option is selected in the dropdown, the value is gotten from the object returned to us. Next, we use the setTheme from our state hook to set the new value to be the value that the state holds.

At this point, we have created our dropdown, set up our theme’s state, and written our function to set the state with the new value. The final thing we need to do to make CodeMirror use our theme is pass the theme to the options object in ControlledEditorComponent. In the options object, let’s add a value named theme, and set its value to the state’s value for the selected theme, also named theme.

Here’s what ControlledEditorComponent would look like now:

<ControlledEditorComponent
  onBeforeChange={handleChange}
  value= {value}
  className="code-mirror-wrapper"
  options={{
    lineWrapping: true,
    lint: true,
    mode: language,
    lineNumbers: true,
    theme: theme,
  }}
/>

Now, we have made a dropdown of different themes that can be selected from in the editor.

Here’s what the full code in Editor.js looks like at the moment:

import React, { useState } from 'react';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/dracula.css';
import 'codemirror/theme/material.css';
import 'codemirror/theme/mdn-like.css';
import 'codemirror/theme/the-matrix.css';
import 'codemirror/theme/night.css';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/css/css';
import { Controlled as ControlledEditorComponent } from 'react-codemirror2';


const Editor = ({ language, value, setEditorState }) => {
  const [theme, setTheme] = useState("dracula")
  const handleChange = (editor, data, value) => {
    setEditorState(value);
  }
  const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']
  return (
    <div className="editor-container">
      <div style={{marginBottom: "10px"}}>
        <label for="themes">Choose a theme: </label>
        <select name="theme" onChange={(el) => {
          setTheme(el.target.value)
        }}>
          {
            themeArray.map( theme => (
              <option value={theme}>{theme}</option>
            ))
          }
        </select>
      </div>
      <ControlledEditorComponent
        onBeforeChange={handleChange}
        value= {value}
        className="code-mirror-wrapper"
        options={{
          lineWrapping: true,
          lint: true,
          mode: language,
          lineNumbers: true,
          theme: theme,
        }}
      />
    </div>
  )
}
export default Editor

There’s only one className that we need to style. Go to App.css and add the following style:

.editor-container{
  padding-top: 0.4%;
}

Now that our editors are ready, let’s go back to App.js and use them there.

src > App.js

The first thing we need to do is import the Editor.js component in here:

import Editor from './components/Editor';

In App.js, let’s declare the states that will hold the contents of the HTML, CSS, and JavaScript editors, respectively.

const [html, setHtml] = useState('');
const [css, setCss] = useState('');
const [js, setJs] = useState('');

If you recall, we will need to use these states to hold and supply the contents of our editors.

Next, let’s replace the paragraph (p) tags that we used for the HTML, CSS, and JavaScript in the conditional renderings with the editor components we have just created, and we’ll also pass in the appropriate prop to each instance of the editor component:

function App() {
  ...
  return (
    <div className="App">
      <p>Welcome to the edior</p>

      // This is where the tab buttons container is...

      <div className="editor-container">
        {
          htmlEditorIsOpen ? (
            <Editor
              language="xml"
              value={html}
              setEditorState={setHtml}
            />
          ) : cssEditorIsOpen ? (
            <Editor
              language="css"
              value={css}
              setEditorState={setCss}
            />
          ) : (
            <Editor
              language="javascript"
              value={js}
              setEditorState={setJs}
            />
          )
        }
      </div>
    </div>
  );
}
export default App;

If you’ve been following along until now, you’ll understand what we did in the code block above.

Here it is in plain English: We replaced the p tags (which were there as placeholders) with instances of the editor components. Then, we supplied their language, value, and setEditorState props, respectively, to match their corresponding states.

We've come so far! Here is what our app looks like now:

(Large preview) Introduction to Iframes

We’ll be making use of inline frames (iframes) to display the result of the code entered in the editor.

According to MDN:

The HTML Inline Frame element (<iframe>) represents a nested browsing context, embedding another HTML page into the current one.

How Iframes Work in React

Iframes are normally used with plain HTML. Using Iframes with React doesn’t require many changes, the major one being to convert attribute names to camelcase. An example of this is that srcdoc would become srcDoc.

The Future of Iframes on the Web

Iframes continue to be really useful in web development. Something you might want to check out is Portals. As Daniel Brain explains:

“Portals introduce a powerful new set of capabilities into this mix. Now it’s possible to build something that feels like an iframe, that can seamlessly animate and morph and take over the full browser window.”

One of the things Portals tries to solve is the URL bar problem. When using iframe, components rendered in the iframe don’t carry a unique URL in the address bar; as such, this might not be great for the user experience, depending on the use case. Portals is worth checking out, and I’d suggest you do that, but because it is not the focus of our article, this is all I’ll say about it here.

Creating the Iframe to House Our Result

Let’s move ahead with our tutorial by creating an iframe to house the result of our editors.

return (
    <div className="App">
      // ...
      <div>
        <iframe
          srcDoc={srcDoc}
          title="output"
          sandbox="allow-scripts"
          frameBorder="1"
          width="100%"
          height="100%"
        />
      </div>
    </div>
  );

Here, we created the iframe and housed it in a div container tag. In the iframe, we passed some attributes that we need:

  • srcDoc
    The srcDoc attribute is written in camelcase because this is how to write iframe attributes in React. When using an iframe, we can either embed an external web page on the page or render specified HTML content. To load and embed an external page, we would use the src property instead. In our case, we are not loading an external page; rather, we want to create a new internal HTML document that houses our result; for this, we need the srcDoc attribute. This attribute takes the HTML document that we want to embed (we have not created that yet, but we will soon).
  • title
    The title attribute is used to describe the contents of the inline frame.
  • sandbox
    This property has many purposes. In our case, we are using it to allow scripts to run in our iframe with the allow-scripts value. Because we are working with a JavaScript editor, this would come in handy quickly.
  • frameBorder
    This merely defines the border thickness of the iframe.
  • width and height
    This defines the width and height of the iframe.

These terms should now make more sense to you. Let’s move on and declare the state that will hold the HTML template document for srcDoc. If you look closely at the code block above, you’ll see that we passed a value to the srcDoc attribute: srcDoc={srcDoc}. Let’s use our useState() React hook to declare the srcDoc state. To do this, in the App.js file, go to where we defined the other states and add this one:

const [srcDoc, setSrcDoc] = useState(` `);

Now that we have created the state, the next thing to do is display the result in the state whenever we type in the code editor. But what we don’t want is to re-render the component on every single key press. With that in mind, let’s proceed.

Configuring the Iframe to Display the Result

Every time there's a change in any of the editors for the HTML, CSS, and JavaScript, respectively, we want useEffect() to be triggered, and that will render the updated result in the iframe. Let’s write useEffect() to do this in the App.js file:

First, import the useEffect() hook:

import React, { useState,  useEffect } from 'react';

Let’s write useEffect() like so:

useEffect(() => {
    const timeOut = setTimeout(() => {
      setSrcDoc(
        `
          <html>
            <body>${html}</body>
            <style>${css}</style>
            <script>${js}</script>
          </html>
        `
      )
    }, 250);
    return () => clearTimeout(timeOut)
  }, [html, css, js])

Here, we wrote a useEffect() hook that will always run whenever the value states that we declared for the HTML, CSS, and JavaScript editors are changed or updated.

Why did we need to use setTimeout()? Well, if we wrote this without it, then every time a single key press is made in an editor, our iframe would be updated, and that isn’t great for performance generally. So we use setTimeout() to delay the update for 250 milliseconds, giving us enough time to know whether the user is still typing. That is, every time the user presses a key, it restarts the count, so the iframe would only be updated when the user has been idle (not typing) for 250 milliseconds. This is a cool way to avoid having to update the iframe every time a key is pressed.

The next thing we did above was to update srcDoc with the new changes. The srcDoc component, as we explained above, renders specified HTML content in the iframe. In our code, we passed an HTML template, taking the html state that contains the code that the user has typed into the HTML editor and placing it between the body tags of our template. We also took the css state that contains the styles that the user has typed in the CSS editor, and we passed that between the style tags. Finally, we took the js state that contains the JavaScript code that the user has typed in the JavaScript editor, and we passed it between the script tags.

Notice that in setting setSrcDoc, we used backticks (` `) instead of normal quotes (' '). This is because backticks allow us to pass in corresponding state values, as we did in the code above.

The return statement in the useEffect() hook is a cleanup function that clears setTimeout() when it is complete, to avoid memory leakage. The documentation has more about useEffect.

Here’s what our project looks like at the moment:

(Large preview)

CodeMirror Addons

With CodeMirror addons, we can enhance our editor with more of the kind of functionality we would find in other code editors. Let’s walk through an example of closing tags being added automatically when an opening tag is typed, and another example of a bracket automatically closing when the opening bracket is inputted:

The first thing to do is import the addon for this into our App.js file:

import 'codemirror/addon/edit/closetag';
import 'codemirror/addon/edit/closebrackets';

Let’s pass it in the ControlledEditorComponent options:

<ControlledEditorComponent
        ...
        options={{
          ...
          autoCloseTags: true,
          autoCloseBrackets: true,
        }}
      />

Now here’s what we have:

(Large preview)

You could add a ton of these addons to your editor to give it richer features. We couldn’t possibly go through all of them here.

Now that we are done with this, let’s briefly discuss things we could do to improve our app’s accessibility and performance.

Performance and Accessibility of the Solution

Looking at our web code editor, some things could definitely be improved upon.

Because we’ve paid attention primarily to functionality, we might have neglected design a little bit. For better accessibility, here are some things you could do to improve this solution:

  1. You could set an active class on the button for the currently open editor. Highlighting the button would improve accessibility by giving users a clear indication of which editor they’re currently working on.
  2. You might want the editor to occupy more screen space than what we have here. Another thing you could try is making the iframe pop up with the click of a button that is docked somewhere to the side. Doing so would give the editor more screen space.
  3. This sort of editor would be useful for people who want to run a quick exercise on their mobile device, so fully adapting it to mobile would be necessary (not to mention both of the points about mobile above).
  4. Currently, we are able to switch the theme of the editor component from among the multiple themes we’ve loaded in, but the general theme of the page remains the same. You could enable the user to switch between a dark and light theme for the entire layout. This would be good for accessibility, relieving the strain on people’s eyes from looking at a bright screen for too long.
  5. We didn’t look at security issues with our iframe, mainly because we were loading an internal HTML document in the iframe, rather than an external document. So we don’t need to consider this too carefully because iframes are a good fit for our use case.
  6. With iframes, another consideration would be page-loading time, because the content being loaded in the iframe would normally be out of your control. In our app, this isn’t an issue because our iframe content isn’t external.

Performance and accessibility are worth a lot of consideration when you’re building any application because they will determine how useful and usable your application is to its users.

Shedrack has done a good job of explaining methods for improving and optimizing performance in React apps. It’s worth checking out!

Conclusion

Working through different projects helps us to learn about a wide range of subjects. Now that you’ve gone through this article, feel free to expand upon your experience by experimenting with more add-ons to make the code editor richer, revamping the UI, and fixing the accessibility and performance concerns outlined above.

Here’s the demo on Codesandbox:

Links and Material

Vue.js vs React: Which One to Choose

Vue.js and React have lots of similar features. Although React is a library, it suits building the view of the single-page application the same efficiently as Vue.js.

Common Features In Vue.js and React:

  • They enable writing front-end with JavaScript and utilize the virtual DOM for fast data rendering.
  • Components have the same single file structure and support JSX.
  • Both systems allow uni- and bi-directional data flow for the components.

Let's study the component build principles in detail. At the end of our journey, we will uncover the key difference between React and Vue.js.

React Without Build Tools

Jim Nielsen:

I think you’ll find it quite refreshing to use React A) with a JSX-like syntax, and B) without any kind of build tooling.

Refreshing indeed:

It’s not really the React that’s the hard part to pull off without build tools (although I do wonder what we lose from not tree shaking), it’s the JSX. I’m so used to JSX I think it would be hard for me to work on a front-end JavaScript project without it. But I know some people literally prefer a render function instead. If that’s the case, you could use React.createComponent directly and skip the JSX, or in the case of Preact, use h:

I work on a project that uses Mithril for the JavaScript templating which is a bit like that, and it’s not my favorite syntax, but you totally get used to it (and it’s fast):

Direct Link to ArticlePermalink


The post React Without Build Tools appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

The Future of ReactJS Developers

ReactJS is a declarative, versatile JavaScript framework for building customizable user interfaces ( UI) with greater simplicity and usability. An application for various states provides some benefits in generating basic views and greater convenience in quickly changing and rendering the related components just as data changes.

React.js is a vital part of the above group and not just a coming and going system. Released in 2013, the list of the most creative innovations continues to grow.

Differences Between Static Generated Sites And Server-Side Rendered Apps

Differences Between Static Generated Sites And Server-Side Rendered Apps

Differences Between Static Generated Sites And Server-Side Rendered Apps

Timi Omoyeni

JavaScript currently has three types of applications that you can build with: Single Page Applications (SPAs), pre-rendering/static generated sites and server-side rendered applications. SPAs come with many challenges, one of which is Search Engine Optimization (SEO). Possible solutions are to make use of Static Site Generators or Server-Side Rendering (SSR).

In this article, I’m going to explain them alongside listing their pros and cons so you have a balanced view. We’re going to look at what static generated/pre-rendering is as well as frameworks such as Gatsby and VuePress that help in creating statically generated sites. We’re also going to look at what server-side rendered (SSR) applications are as well as frameworks like Nextjs and Nuxtjs that can help you create SSR applications. Finally, we’re going to cover the differences between these two methods and which of them you should use when building your next application.

Note: You can find all the code snippets in this article on GitHub.

What Is A Static Site Generator?

A Static Site Generator (SSG) is a software application that creates HTML pages from templates or components and a given content source. You give it some text files and content, and the generator will give you back a complete website, and this completed website is referred to as a static generated site. What this means is that your site pages are generated at build time and your site content does not change unless you add new contents or components and “rebuild” or you have to rebuild your site if you want it to be updated with new content.

Diagram explaining how static site generation works
How static site generation works (Large preview)

This approach is good for building applications that the content does not change too often — sites that the content does not have to change depending on the user, and sites that do not have a lot of user-generated content. An example of such a site is a blog or a personal website. Let’s look at some advantages of using static generated sites.

PROS

  • Fast website: Since all of your site’s pages and content have been generated at build time, you do not have to worry about API calls to the server for content and this makes your site very fast.
  • Easy to deploy: After your static site has been generated, you would be left with static files, and hence, it can be easily deployed to platforms like Netlify.
  • Security: Static generated site are solely composed of static files, the risk of being vulnerable to cyber attacks is minimal. This is because static generated sites have no database, attackers cannot inject malicious code or exploit your database.
  • You can use version control software (e.g git) to manage and track changes to your content. This can come in handy when you want to roll back changes you made to the content on your site.

CONS

  • Content can become stale if it changes too quickly.
  • To update its content, you have to rebuild the site.
  • Build time would increase depending on the size of the application.

Examples of static site generators are GatsbyJS and VuePress. Let us take a look at how to create static sites using these two generators.

Gatsby

According to their official website,

“Gatsby is a free and open-source framework based on React that helps developers build blazing-fast websites and apps.”

This means developers familiar with React would find it easy to get started with Gatsby.

To use this generator, you first have to install it using NPM:

npm install -g gatsby-cli

This will install Gatsby globally on your machine, you only have to run this command once on your machine. After this installation is complete, you can create your first static site generator using the following command.

gatsby new demo-gatsby

This command will create a new Gatsby project that I have named demo-gatsby. When this is done, you can start up your app server by running the following command:

cd demo-gatsby
gatsby develop

Your Gatsby application should be running on localhost:8000.

Gatsby default landing page
Gatsby default starter page (Large preview)

The folder structure for this app looks like this;

--| gatsby-browser.js  
--| LICENSE        
--| README.md
--| gatsby-config.js
--| node_modules/  
--| src/
----| components
----| pages
----| images
--| gatsby-node.js     
--| package.json   
--| yarn.lock
--| gatsby-ssr.js      
--| public/
----| icons
----| page-data
----| static

For this tutorial, we’re only going to look at the src/pages folder. This folder contains files that would be generated into routes on your site.

To test this, let us add a new file (newPage.js) to this folder:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const NewPage = () => (
  <Layout>
    <SEO title="My New Page" />
    <h1>Hello Gatsby</h1>
    <p>This is my first Gatsby Page</p>
    <button>
      <Link to='/'>Home</Link>
    </button>
  </Layout>
)
export default NewPage

Here, we import React from the react package so when your code is transpiled to pure JavaScript, references to React will appear there. We also import a Link component from gatsby and this is one of React’s route tag that is used in place of the native anchor tag ( <a href='#'>Link</a>). It accepts a to prop that takes a route as a value.

We import a Layout component that was added to your app by default. This component handles the layout of pages nested inside it. We also import the SEO component into this new file. This component accepts a title prop and configures this value as part of your page’s metadata. Finally, we export the function NewPage that returns a JSX containing your new page’s content.

And in your index.js file, add a link to this new page we just created:

import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"
const IndexPage = () => (
  <Layout>
    <SEO title="Home" />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
    {/* new link */}
    <button>
      <Link to="/newPage/">Go to New Page</Link>
    </button>
  </Layout>
)
export default IndexPage

Here, we import the same components that were used in newPage.js file and they perform the same function in this file. We also import an Image component from our components folder. This component is added by default to your Gatsby application and it helps in lazy loading images and serving reduced file size. Finally, we export a function IndexPage that returns JSX containing our new link and some default content.

Now, if we open our browser, we should see our new link at the bottom of the page.

Gatsby default landing page with link to a new page
Gatsby landing page with new link (Large preview)

And if you click on Go To New Page, it should take you to your newly added page.

New page containing some texts
New gatsby page (Large preview)

VuePress

VuePress is a static site generator that is powered by Vue, Vue Router and Webpack. It requires little to no configuration for you to get started with it. While there are a number of tools that are static site generators, VuePress stands out from amongst the pack for a single reason: its primary directive is to make it easier for developers to create and maintain great documentation for their projects.

To use VuePress, you first have to install it:

//globally
yarn global add vuepress # OR npm install -g vuepress

//in an existing project
yarn add -D vuepress # OR npm install -D vuepress

Once the installation process is done, you can run the following command in your terminal:

# create the project folder
mkdir demo-vuepress && cd demo-vuepress

# create a markdown file
echo '# Hello VuePress' > README.md

# start writing
vuepress dev

Here, we create a folder for our VuePress application, add a README.md file with # Hello VuePress as the only content inside this file, and finally, start up our server.

When this is done, our application should be running on localhost:8080 and we should see this in our browser:

A VuePress webpage with a text saying ‘Hello VuePress’
VuePress landing page (Large preview)

VuePress supports VueJS syntax and markup inside this file. Update your README.md file with the following:

# Hello VuePress
_VuePress Rocks_
> **Yes!**
_It supports JavaScript interpolation code_
> **{{new Date()}}**
<p v-for="i of ['v','u', 'e', 'p', 'r', 'e', 's', 's']">{{i}}</p>

If you go back to your browser, your page should look like this:

Updated VuePress page
Updated Vuepress page (Large preview)

To add a new page to your VuePress site, you add a new markdown file to the root directory and name it whatever you want the route to be. In this case, I’ve gone ahead to name it Page-2.md and added the following to the file:

# hello World
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.

And now, if you navigate to /page-2 in your browser, we should see this:

A VuePress webpage containing hello world
A “Hello World” page in VuePress (Large preview)

What Is Server-Side Rendering? (SSR)

Server-Side Rendering (SSR), is the process of displaying web-pages on the server and passing it to the browser/client-side instead of rendering it in the browser. Server-side sends a fully rendered page to the client; the client’s JavaScript bundle takes over and allows the SPA framework to operate.

This means if you have an application that is server-side rendered, your content is fetched on the server side and passed to your browser to display to your user. With client-side rendering it is different, you would have to navigate to that page first before it fetches data from your server meaning your user would have to wait for some seconds before they’re served with the content on that page. Applications that have SSR enabled are called Server-side rendered applications.

A diagram explaining how server-side rendering works
How SSR works (Large preview)

This approach is good for building complex applications that require user interaction, rely on a database, or where the content changes very often. This is because content on these sites changes very often and the users need to see the updated content as soon as they’re updated. It is also good for applications that have tailored content depending on who is viewing it and applications where you need to store user-specific data like email and user preference while also catering for SEO. An example of this is a large e-commerce platform or a social media site. Let us look at some of the advantages of server-side rendering your applications.

Pros

  • Content is up to date because it fetches content on the go;
  • Your site loads fast because it fetches its content on the server-side before rendering it to the user;
  • Since in SSR JavaScript is rendered server-side, your users’ devices have little relevance to the load time of your page and this leads to better performance.

CONS

  • More API calls to the server since they’re made per request;
  • Cannot deploy to a static CDN.

Further examples of frameworks that offer SSR are Next.js and Nuxt.js.

Next.js

Next.js is a React.js framework that helps in building static sites, server-side rendered applications, and so on. Since it was built on React, knowledge of React is required to use this framework.

To create a Next.js app, you need to run the following:

npm init next-app
# or
yarn create next-app

You would be prompted to choose a name your application, I have named my application demo-next. The next option would be to select a template and I’ve selected the Default starter app after which it begins to set up your app. When this is done, we can now start our application

cd demo-next
yarn dev 
# or npm run dev

Your application should be running on localhost:3000 and you should see this in your browser;

Default Nextjs landing page
Next.js landing page (Large preview)

The page that is being rendered can be found in pages/index.js so if you open this file and modify the JSX inside the Home function, it would reflect in your browser. Replace the JSX with this:

import Head from 'next/head'
export default function Home() {
  return (
    <div className="container">
      <Head>
        <title>Hello Next.js</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <h1 className="title">
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <p className='description'>Nextjs Rocks!</p>
      </main>
      <style jsx>{`
        main {
          padding: 5rem 0;
          flex: 1;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
        }
        .title a {
          color: #0070f3;
          text-decoration: none;
        }
        .title a:hover,
        .title a:focus,
        .title a:active {
          text-decoration: underline;
        }
        .title {
          margin: 0;
          line-height: 1.15;
          font-size: 4rem;
        }
        .title,
        .description {
          text-align: center;
        }
        .description {
          line-height: 1.5;
          font-size: 1.5rem;
        }
      `}</style>
      <style jsx global>{`
        html,
        body {
          padding: 0;
          margin: 0;
          font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
            Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
            sans-serif;
        }
        * {
          box-sizing: border-box;
        }
      `}</style>
    </div>
  )
}

In this file, we make use of Next.js Head component to set our page’s metadata title and favicon for this page. We also export a Home function that returns a JSX containing our page’s content. This JSX contains our Head component together with our main page’s content. It also contains two style tags, one for styling this page and the other for the global styling of the app.

Now, you should see that the content on your app has changed to this:

Nextjs landing page containing ‘welcome to Nextjs’ text
Updated landing page (Large preview)

Now if we want to add a new page to our app, we have to add a new file inside the /pages folder. Routes are automatically created based on the /pages folder structure, this means that if you have a folder structure that looks like this:

--| pages
----| index.js ==> '/'
----| about.js ==> '/about'
----| projects
------| next.js ==> '/projects/next'

So in your pages folder, add a new file and name it hello.js then add the following to it:

import Head from 'next/head'
export default function Hello() {
  return (
    <div>
       <Head>
        <title>Hello World</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className='container'>
        <h1 className='title'>
         Hello <a href="https://en.wikipedia.org/wiki/Hello_World_(film)">World</a>
        </h1>
        <p className='subtitle'>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatem provident soluta, sit explicabo impedit nobis accusantium? Nihil beatae, accusamus modi assumenda, optio omnis aliquid nobis magnam facilis ipsam eum saepe!</p>
      </main>
      <style jsx> {`
      
      .container {
        margin: 0 auto;
        min-height: 100vh;
        max-width: 800px;
        text-align: center;
      }
      .title {
        font-family: "Quicksand", "Source Sans Pro", -apple-system, BlinkMacSystemFont,
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        display: block;
        font-weight: 300;
        font-size: 100px;
        color: #35495e;
        letter-spacing: 1px;
      }
      .subtitle {
        font-weight: 300;
        font-size: 22px;
        color: #526488;
        word-spacing: 5px;
        padding-bottom: 15px;
      }
      `} </style>
    </div>
  )
}

This page is identical to the landing page we already have, we only changed the content and added new styling to the JSX. Now if we visit localhost:3000/hello, we should see our new page:

A Nextjs webpage containing ‘Hello world’
A “Hello World ” page in Next.js (Large preview)

Finally, we need to add a link to this new page on our index.js page, and to do this, we make use of Next’s Link component. To do that, we have to import it first.

# index.js
import Link from 'next/link'

#Add this to your JSX
<Link href='/hello'>
<Link href='/hello'>
  <a>Next</a>
</Link>

This link component is how we add links to pages created in Next in our application.

Now if we go back to our homepage and click on this link, it would take us to our /hello page.

Nuxt.js

According to their official documentation:

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt’s goal is to make web development powerful and performant with a great developer experience in mind.”

It is based on Vue.js so that means Vue.js developers would find it easy getting started with it and knowledge of Vue.js is required to use this framework.

To create a Nuxt.js app, you need to run the following command in your terminal:

yarn create nuxt-app <project-name>
# or npx
npx create-nuxt-app <project-name>

This would prompt you to select a name along with some other options. I named mine demo-nuxt and selected default options for the other options. When this is done, you can open your app folder and open pages/index.vue. Every file in this folder file is turned into a route and so our landing page is controlled by index.vue file. So if you update it with the following:

<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        Hello Nuxt
      </h1>
      <h2 class="subtitle">
        Nuxt.js ROcks!
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>
<script>
import Logo from '~/components/Logo.vue'
export default {
  components: {
    Logo
  }
}
</script>
<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}
.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}
.links {
  padding-top: 15px;
}
</style>

And run your application:

cd demo-nuxt
# start your applicatio
yarn dev # or npm run dev

Your application should be running on localhost:3000 and you should see this:

Default Nuxtjs landing page
Nuxt.js landing page (Large preview)

We can see that this page displays the content we added in to index.vue. The router structure works the same way Next.js router works; it renders every file inside /pages folder into a page. So let us add a new page (hello.vue) to our application.

<template>
  <div>
    <h1>Hello World!</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Id ipsa vitae tempora perferendis, voluptate a accusantium itaque vel ex, provident autem quod rem saepe ullam hic explicabo voluptas, libero distinctio?</p>
  </div>
</template>
<script>
export default {};
</script>
<style>
</style>

So if you open localhost:3000/hello, you should see your new page in your browser.

A Nuxtjs webpage containing ‘Hello World’
“Hello World” page in Nuxtjs (Large preview)

Taking A Closer Look At The Differences

Now that we have looked at both static-site generators and server-side rendering and how to get started with them by using some popular tools, let us look at the differences between them.

Static Sites Generators Server-Side Rendering
Can easily be deployed to a static CDN Cannot be deployed to a static CDN
Content and pages are generated at build time Content and pages are generated per request
Content can become stale quickly Content is always up to date
Fewer API calls since it only makes it at build time Makes API calls each time a new page is visited

Conclusion

We can see why it is so easy to think both static generated sites and server-side rendered applications are the same. Now that we know the differences between them are, I would advise that we try to learn more on how to build both static generated sites and server-side rendered applications in order to fully understand the differences between them.

Further Resources

Here are some useful links that are bound to help you get started in no time:

Smashing Editorial (ks, ra, il)

CSS2JS

To add inline styles on an element in JSX, you have to do it in this object syntax, like:

<div style={{
  fontSize: 16,
  marginBottom: "1rem"
}}>
  Content
</div>

That might look a little weird to us folks who are so used to the CSS syntax, where it is font-size (not fontSize), margin-bottom (not marginBottom), and semi-colons (not commas).

That's not JSX (or React or whatever) being weird — that's just how styles are in JavaScript. If you wanted to set the font-size from any other JavaScript, you'd have to do:

div.style.fontSize = "16px";

I say that, but other APIs do want you to use the CSS syntax, like:

window.getComputedStyle(document.body)
  .getPropertyValue("font-size");

There are also lots of CSS-in-JS libraries that either require or optionally support setting styles in this object format. I've even heard that with libraries that support both the CSS format (via template literals) and the object format (e.g. Emotion), that some people prefer the object syntax over the CSS syntax because it feels more at home in the surrounding JavaScript and is a bit less verbose when doing stuff like logic or injecting variables.

Anyway, the actual reason for the post is this little website I came across that converts the CSS format to the object format. CSS2JS:

Definitely handy if you had a big block of styles to convert.

Direct Link to ArticlePermalink

The post CSS2JS appeared first on CSS-Tricks.

Using JSX With Vue.js

Love it or hate it, JSX is a popular extension to JavaScript that allows XML tokens in your scripts.

If you want to create templates in your script files and you find Vue's render() function to be difficult to work with, JSX may be just what you need.

Prettier

Prettier has become the predominant code formatting tool used by web designers and developers. It has a nice set of defaults that work great for CSS, JavaScript, and even HTML (and most of the preprocessor we support, like SCSS).

We've long offered one-click access to formatting, and now those are powered by Prettier.

Here's an extreme example (video) of minimized CSS:

Your CSS is more likely to have minor indentation weirdnesses and such, but hey this thing can whip anything into shape. As long as it's valid, that is, which is an important point: if your code has syntax errors, we won't format it. It's just a silent failure for now, we'll have to see if we can improve the error messaging there, but if your code doesn't format after selecting the option, that's probably why.

Here's some vanilla JavaScript:

And here's some HTML:

Note the formatting is a little unusual for some folks, with attributes on their own line and force-truncated line lengths. That's valid HTML and how it's commonly seen in JSX.

The post Prettier appeared first on CodePen Blog.

Domain-Driven Design With React

There is very little guidance on how to organize front-end applications in the world of React. (Just move files around until it “feels right,” lol). The truth is that we can do better. Let’s take a look at one pattern you might consider using to architect your site.

At first, you might split up your code between /components and /containers folders. This works for small sites but you’ll find yourself look for something more robust when scaling to larger sites. Luckily, decades of research into systems design has given us a wealth of patterns to explore to create a scalable architecture.

One of those is domain-driven design, and it has regained popularity over the last few years. Let’s explore how we can use it in React-land.

A primer on domain-driven design

Domain-driven design (DDD) is the practice of managing complexity of software applications by relating their underlying data models to domain logic. That’s a mouthful, so let’s break it down further.

The domain is an ontology, meaning how things are grouped in the world. For example, the word joist has a very specific connection to the domain of building construction. Another word, like Mike, can belong to multiple domains, such as the domain of Biblical names (short for Michael), or in the domain of politics as it relates to the NATO phonetic alphabet.

When the design is domain-driven, it means we place the model of our domain (e.g. a playing card in the domain of Poker) in a context (e.g. the contextual grouping, such as a Game) to help manage the complexity.

Organizing a DDD site

Domain-driven design is specifically for handling the complexity of growing sites as they add more and more models. It doesn’t really make sense for a site with one model. Once you get to about four models, that’s a good time to start looking at binding your models to multiple contexts. This isn’t a hard-and-fast rule, so don’t feel like you have to break out into multiple contexts, but once you get above four models, those contextual groupings will begin to surface.

Start by organizing your domains

Let’s use Twitter as our example site to organize. One way to separate domains within Twitter is to split up our models between the Blog platform that powers the Tweets and the Interaction elements that enable the micro-blogging to spread and flourish.

Is this the only way to separate concerns in Twitter? Definitely not! One key aspect of DDD is that there is no one correct way to create domains. There are plenty of ways to split up the bounded contexts of an application, so don’t focus too much on the architecture we’ve chosen. Instead, use this as a springboard to understand how we can apply DDD to the organization of our front-end code.

That said, our code will now be structured to look like this (assuming you start with something like create-react-app):

twitter/
├── App.css
├── App.js
├── App.test.js
├── blog/
└── interaction/

Define the components and containers in each domain

Now that we have our basic folder structure set up, it is time to add some real components! Looking at our domain UML diagram above, it would be useful to start with containers that fetch data on a given page and components that organize the templates that compose those pages. Expanding on our app, we now have the following structure in place (omitting our accompanying test.js files for simplicity):

twitter/
├── App.css
├── App.js
├── App.test.js
├── blog/
│   ├── HomePage.js
│   ├── TweetCard.js
│   ├── TweetDialog.js
│   ├── TweetList.js
│   ├── TweetListItem.js
│   ├── UserPage.js
│   └── UserCard.js
└── interaction/
    ├── FollowButton.js
    ├── LikeButton.js
    └── ShareButton.js

We still keep our App file to initialize React to our root-level HTML tag. With our domains in place, we begin to build our containers (such as HomePage and UserPage) and components (such as TweetCard and TweetListItem). Alternatively, we could further segment the models within our domains to look like so:

twitter/
└── blog/
    ├── user/
    │   ├── HomePage.js
    │   ├── UserCard.js
    │   └── UserPage.js
    └── tweet/
        ├── TweetCard.js
        ├── TweetDialog.js
        ├── TweetList.js
        └── TweetListItem.js

But given the size of the application it isn’t necessary at this stage.

Add helpers, if they’re needed

As we build out our application our UIs will continue to increase in complexity. To deal with this, we have two methods for separating concerns and pulling logic out of our component templates: presenters and utilities. Presenters push all of the visual presentation logic out of the templates to keep the view layer as clean and simple as possible. Utilities collect shared functionality for all other logic on the front end that is not specifically related to the templates. Let’s examine these a bit closer.

Clean up templates with presenters

Think of a Twitter profile. What kinds of elements do you see here on my account?

There’s information directly related to my user: name, handle, description, location, website, birthday, start date. There are also counts of associations between other models — how many other users are following me? How many other users am I following? There’s additional logic that isn’t even captured on the page, such as my tweets, replies, media uploads, and content I’ve liked. To capture all of this presentational logic appropriately, we can add an additional file within our file tree to isolate our presenter pattern from the JSX component:

twitter/
└── blog/
    ├── user/
    │   ├── UserCard.js
    │   ├── UserCard.presenter.js

Push out logic into utilities

Certain presentational logic is so fundamental that it could be useful across applications regardless of whether or not it is used within rendering. Currency formatting, validations, and timestamp formatting are all use cases where we could benefit from isolated utility functions across our application. Where do those live? Since they span domains, utilities can be in their own folder:

    twitter/
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── blog/
    │   ├── HomePage.js
    │   ├── TweetCard.js
    │   ├── TweetDialog.js
    │   ├── TweetList.js
    │   ├── TweetListItem.js
    │   ├── UserCard.js
    │   ├── UserCard.presenterjs
    │   └── UserPage.js
    ├── interaction/
    │   ├── FollowButton.js
    │   ├── LikeButton.js
    │   └── ShareButton.js
    └── utils/
         ├── currency.js
         ├── time.js
         └── validation.js

There is no wrong way to organize your app!

Ultimately, the choice is yours. This is just one example for myriad ways you could arrange your application. Domain-driven design is a valuable tool because it separates business logic in a meaningful way, creates a clearer distinction for domain expertise amongst developers, and provides rules for easily organizing and scaling your code.

But if you’re looking for an alternative to the traditional chaos of React application file structures, take a look at domain-driven design. It may be just the thing.

Lastly, if you like this sort of content and want to learn more about front-end, user interface development, and UX design and research (organized by your experience in the industry), I run a free newsletter that you might want to check out.

The post Domain-Driven Design With React appeared first on CSS-Tricks.

Making a Chart? Try Using Mobx State Tree to Power the Data

Who loves charts? Everyone, right? There are lots of ways to create them, including a number of libraries. There’s D3.js, Chart.js, amCharts, Highcharts, and Chartist, to name only a few of many, many options.

But we don’t necessary need a chart library to create charts. Take Mobx-state-tree (MST), an intuitive alternative to Redux for managing state in React. We can build an interactive custom chart with simple SVG elements, using MST to manage and manipulate data for the chart. If you've attempted to build charts using something like D3.js in the past, I think you’ll find this approach more intuitive. Even if you're an experienced D3.js developer, I still think you'll be interested to see how powerful MST can be as a data architecture for visualizations.

Here’s an example of MST being used to power a chart:

This example uses D3's scale functions but the chart itself is rendered simply using SVG elements within JSX. I don’t know of any chart library that has an option for flashing hamster points so this is a great example of why it’s great to build your own charts — and it’s not as hard as you might think!

I’ve been building charts with D3 for over 10 years and, while I love how powerful it is, I’ve always found that my code can end up being unwieldy and hard to maintain, especially when working with complex visualizations. MST has changed all that completely by providing an elegant way to separate the data handling from the rendering. My hope for this article is that it will encourage you to give it a spin.

Getting familiar with MST model

First of all, let’s cover a quick overview of what a MST model looks like. This isn’t an in-depth tutorial on all things MST. I only want to show the basics because, really, that’s all you need about 90% of the time.

Below is a Sandbox with the code for a simple to-do list built in MST. Take a quick look and then I’ve explain what each section does.

First of all, the shape of the object is defined with typed definitions of the attribute of the model. In plain English, this means an instance of the to-do model must have a title, which must be a string and will default to having a “done” attribute of false.

.model("Todo", {
  title: types.string,
  done: false //this is equivalent to types.boolean that defaults to false
})

Next, we have the view and action functions. View functions are ways to access calculated values based on data within the model without making any changes to the data held by the model. You can think of them as read-only functions.

.views(self => ({
  outstandingTodoCount() {
    return self.todos.length - self.todos.filter(t => t.done).length;
  }
}))

Action functions, on the other hand, allow us to safely update the data. This is always done in the background in a non-mutable way.

.actions(self => ({
  addTodo(title) {
    self.todos.push({
      id: Math.random(),
      title
    });
  }
}));

Finally, we create a new instance of the store:

const todoStore = TodoStore.create({
  todos: [
    {
      title: "foo",
      done: false
    }
  ]
});

To show the store in action, I’ve added a couple of console logs to show the output of outStandingTodoCount() before and after triggering the toggle function of the first instance of a Todo.

console.log(todoStore.outstandingTodoCount()); // outputs: 1
todoStore.todos[0].toggle();
console.log(todoStore.outstandingTodoCount()); // outputs: 0

As you can see, MST gives us a data structure that allows us to easily access and manipulate data. More importantly, it’s structure is very intuitive and the code is easy to read at a glance — not a reducer in sight!

Let’s make a React chart component

OK, so now that we have a bit of background on what MST looks like, let’s use it to create a store that manages data for a chart. We’ll will start with the chart JSX, though, because it’s much easier to build the store once you know what data is needed.

Let’s look at the JSX which renders the chart.

The first thing to note is that we are using styled-components to organize our CSS. If that’s new to you, Cliff Hall has a great post that shows it in use with a React app.

First of all, we are rendering the dropdown that will change the chart axes. This is a fairly simple HTML dropdown wrapped in a styled component. The thing to note is that this is a controlled input, with the state set using the selectedAxes value from our model (we’ll look at this later).

<select
  onChange={e =>
    model.setSelectedAxes(parseInt(e.target.value, 10))
  }
  defaultValue={model.selectedAxes}
>

Next, we have the chart itself. I’ve split up the axes and points in to their own components, which live in a separate file. This really helps keep the code maintainable by keeping each file nice and small. Additionally, it means we can reuse the axes if we want to, say, have a line chart instead of points. This really pays off when working on large projects with multiple types of chart. It also makes it easy to test the components in isolation, both programmatically and manually within a living style guide.

{model.ready ? (
  <div>
    <Axes
      yTicks={model.getYAxis()}
      xTicks={model.getXAxis()}
      xLabel={xAxisLabels[model.selectedAxes]}
      yLabel={yAxisLabels[model.selectedAxes]}
    ></Axes>
    <Points points={model.getPoints()}></Points>
  </div>
) : (
  <Loading></Loading>
)}

Try commenting out the axes and points components in the Sandbox above to see how they work independently of each other.

Lastly, we’ll wrap the component with an observer function. This means that any changes in the model will trigger a re-render.

export default observer(HeartrateChart);

Let’s take a look at the Axes component:

As you can see, we have an XAxis and a YAxis. Each has a label and a set of tick marks. We go into how the marks are created later, but here you should note that each axis is made up of a set of ticks, generated by mapping over an array of objects with a label and either an x or y value, depending on which axis we are rendering.

Try changing some of the attribute values for the elements and see what happens… or breaks! For example, change the line element in the YAxis to the following:

<line x1={30} x2="95%" y1={0} y2={y} />

The best way to learn how to build visuals with SVG is simply to experiment and break things. 🙂

OK, that’s half of the chart. Now we’ll look at the Points component.

Each point on the chart is composed of two things: an SVG image and a circle element. The image is the animal icon and the circle provides the pulse animation that is visible when mousing over the icon.

Try commenting out the image element and then the circle element to see what happens.

This time the model has to provide an array of point objects which gives us four properties: x and y values used to position the point on the graph, a label for the point (the name of the animal) and pulse, which is the duration of the pulse animation for each animal icon. Hopefully this all seems intuitive and logical.

Again, try fiddling with attribute values to see what changes and breaks. You can try setting the y attribute of the image to 0. Trust me, this is a much less intimidating way to learn than reading the W3C specification for an SVG image element!

Hopefully this gives you an understanding and feel for how we are rendering the chart in React. Now, it’s just a case of creating a model with the appropriate actions to generate the points and ticks data we need to loop over in JSX.

Creating our store

Here is the complete code for the store:

I’ll break down the code into the three parts mentioned earlier:

  1. Defining the attributes of the model
  2. Defining the actions
  3. Defining the views

Defining the attributes of the model

Everything we define here is accessible externally as a property of the instance of the model and — if using an observable wrapped component — any changes to these properties will trigger a re-render.

.model('ChartModel', {
  animals: types.array(AnimalModel),
  paddingAndMargins: types.frozen({
    paddingX: 30,
    paddingRight: 0,
    marginX: 30,
    marginY: 30,
    marginTop: 30,
    chartHeight: 500
  }),
  ready: false, // means a types.boolean that defaults to false
  selectedAxes: 0 // means a types.number that defaults to 0
})

Each animal has four data points: name (Creature), longevity (Longevity__Years_), weight (Mass__grams_), and resting heart rate (Resting_Heart_Rate__BPM_).

const AnimalModel = types.model('AnimalModel', {
  Creature: types.string,
  Longevity__Years_: types.number,
  Mass__grams_: types.number,
  Resting_Heart_Rate__BPM_: types.number
});

Defining the actions

We only have two actions. The first (setSelectedAxes ) is called when changing the dropdown menu, which updates the selectedAxes attribute which, in turn, dictates what data gets used to render the axes.

setSelectedAxes(val) {
  self.selectedAxes = val;
},

The setUpScales action requires a bit more explanation. This function is called just after the chart component mounts, within a useEffect hook function, or after the window is resized. It accepts an object with the width of the DOM that contains the element. This allows us to set up the scale functions for each axis to fill the full available width. I will explain the scale functions shortly.

In order to set up scale functions, we need to calculate the maximum value for each data type, so the first thing we do is loop over the animals to calculate these maximum and minimum values. We can use zero as the minimum value for any scale we want to start at zero.

// ...
self.animals.forEach(
  ({
    Creature,
    Longevity__Years_,
    Mass__grams_,
    Resting_Heart_Rate__BPM_,
    ...rest
  }) => {
    maxHeartrate = Math.max(
      maxHeartrate,
      parseInt(Resting_Heart_Rate__BPM_, 10)
    );
    maxLongevity = Math.max(
      maxLongevity,
      parseInt(Longevity__Years_, 10)
    );
    maxWeight = Math.max(maxWeight, parseInt(Mass__grams_, 10));
    minWeight =
      minWeight === 0
        ? parseInt(Mass__grams_, 10)
        : Math.min(minWeight, parseInt(Mass__grams_, 10));
  }
);
// ...

Now to set up the scale functions! Here, we’ll be using the scaleLinear and scaleLog functions from D3.js. When setting these up, we specify the domain, which is the minimum and maximum input the functions can expect, and the range, which is the maximum and minimum output.

For example, when I call self.heartScaleY with the maxHeartrate value, the output will be equal to marginTop. That makes sense because this will be at the very top of the chart. For the longevity attribute, we need to have two scale functions since this data will appear on either the x- or the y-axis, depending on which dropdown option is chosen.

self.heartScaleY = scaleLinear()
  .domain([maxHeartrate, minHeartrate])
  .range([marginTop, chartHeight - marginY - marginTop]);
self.longevityScaleX = scaleLinear()
  .domain([minLongevity, maxLongevity])
  .range([paddingX + marginY, width - marginX - paddingX - paddingRight]);
self.longevityScaleY = scaleLinear()
  .domain([maxLongevity, minLongevity])
  .range([marginTop, chartHeight - marginY - marginTop]);
self.weightScaleX = scaleLog()
  .base(2)
  .domain([minWeight, maxWeight])
  .range([paddingX + marginY, width - marginX - paddingX - paddingRight]);

Finally, we set self.ready to be true since the chart is ready to render.

Defining the views

We have two sets of functions for the views. The first set outputs the data needed to render the axis ticks (I said we’d get there!) and the second set outputs the data needed to render the points. We’ll take a look at the tick functions first.

There are only two tick functions that are called from the React app: getXAxis and getYAxis. These simply return the output of other view functions depending on the value of self.selectedAxes.

getXAxis() {
  switch (self.selectedAxes) {
    case 0:
      return self.longevityXAxis;
      break;
    case 1:
    case 2:
      return self.weightXAxis;
      break;
  }
},
getYAxis() {
  switch (self.selectedAxes) {
    case 0:
    case 1:
      return self.heartYAxis;
      break;
    case 2:
      return self.longevityYAxis;
      break;
  }
},

If we take a look at the Axis functions themselves we can see they use a ticks method of the scale function. This returns an array of numbers suitable for an axis. We then map over the values to return the data we need for our axis component.

heartYAxis() {
  return self.heartScaleY.ticks(10).map(val => ({
    label: val,
    y: self.heartScaleY(val)
  }));
}
// ...

Try changing the value of the parameter for the ticks function to 5 and see how it affects the chart: self.heartScaleY.ticks(5).

Now we have the view functions to return the data needed for the Points component.

If we take a look at longevityHeartratePoints (which returns the point data for the “Longevity vs. Heart” rate chart), we can see that we are looping over the array of animals and using the appropriate scale functions to get the x and y positions for the point. For the pulse attribute, we use some maths to convert the beats per minute value of the heart rate into a value representing the duration of a single heartbeat in milliseconds.

longevityHeartratePoints() {
  return self.animals.map(
    ({ Creature, Longevity__Years_, Resting_Heart_Rate__BPM_ }) => ({
      y: self.heartScaleY(Resting_Heart_Rate__BPM_),
      x: self.longevityScaleX(Longevity__Years_),
      pulse: Math.round(1000 / (Resting_Heart_Rate__BPM_ / 60)),
      label: Creature
    })
  );
},

At the end of the store.js file, we need to create a Store model and then instantiate it with the raw data for the animal objects. It is a common pattern to attach all models to a parent Store model which can then be accessed through a provider at top level if needed.

const Store = types.model('Store', {
  chartModel: ChartModel
});
const store = Store.create({
  chartModel: { animals: data }
});
export default store;

And that is it! Here’s our demo once again:


This is by no means the only way to organize data to build charts in JSX, but I have found it to be incredibly effective. I’ve have used this structure and stack in the wild to build a library of custom charts for a big corporate client and was blown away with how nicely MST worked for this purpose. I hope you have the same experience!

The post Making a Chart? Try Using Mobx State Tree to Power the Data appeared first on CSS-Tricks.

Some HTML is “Optional”

There is a variety of HTML that you can just leave out of the source HTML and it's still valid markup.

Doesn't this look weird?

<p>Paragraph one.
<p>Paragraph two.
<p>Paragraph three.

It does to me, but the closing </p> tags are optional. The browser will detect it needs them and manifest correctly in the DOM anyway.

This probably happens to HTML you write and you don't even know it. For example...

<table>
  <tr>
    <td></td>
  </tr>
</table>

That looks perfectly fine to me, but the browser will inject a <tbody> in there around that <tr> for you. Optional in HTML, but the DOM will put it in anyway.

Heck, you don't really even need a <body> in the same fashion! Jens Oliver Meiert shares more:

<link rel=stylesheet href=default.css>

Some attributes are "optional" too in the sense that they have defaults you can leave out. For example, a <button> is automatically <button type="submit">.

Jens further argues that these are almost considered optimizations, as it reduces file size and thus network speed.

Me, I don't like looking at HTML like that. Makes me nervous, since there are actual situations that screw up if you don't do it right. Not all file names can be left unquoted. Sometimes, leaving off closing tags means enveloping a sibling element in a way you didn't expect. I'd even sacrifice a tiny smidge of performance for a more resilient site. Sorta like how I know that * {} isn't a particularly efficient selector, but worrying about CSS selector performance is misplaced worry in most cases (the speed difference is negligible).

I actually quite like JSX in how strict it forces you to write "HTML." That strictness helps code formatting (e.g. Prettier) too, as a bonus.

But hey, a perf gain is a perf gain, so I wouldn't say no to tooling that automatically does this stuff to compiled output. That's apparently something HTMLminifier can do.

The post Some HTML is “Optional” appeared first on CSS-Tricks.