react – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Fri, 10 Mar 2023 16:41:56 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 react – CSS-Tricks https://css-tricks.com 32 32 45537868 5 Mistakes I Made When Starting My First React Project https://css-tricks.com/5-mistakes-starting-react/ https://css-tricks.com/5-mistakes-starting-react/#respond Fri, 10 Mar 2023 16:41:52 +0000 https://css-tricks.com/?p=377098 You know what it’s like to pick up a new language or framework. Sometimes there’s great documentation to help you find your way through it. But even the best documentation doesn’t cover absolutely everything. And when you work with something …


5 Mistakes I Made When Starting My First React Project originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
You know what it’s like to pick up a new language or framework. Sometimes there’s great documentation to help you find your way through it. But even the best documentation doesn’t cover absolutely everything. And when you work with something that’s new, you’re bound to find a problem that doesn’t have a written solution.

That’s how it was for me the first time I created a React project — and React is one of those frameworks with remarkable documentation, especially now with the beta docs. But I still struggled my way through. It’s been quite a while since that project, but the lessons I gained from it are still fresh in my mind. And even though there are a lot of React “how-to” tutorials in out there, I thought I’d share what I wish I knew when I first used it.

So, that’s what this article is — a list of the early mistakes I made. I hope they help make learning React a lot smoother for you.

Using create-react-app to start a project

TL;DR Use Vite or Parcel.

Create React App (CRA) is a tool that helps you set up a new React project. It creates a development environment with the best configuration options for most React projects. This means you don’t have to spend time configuring anything yourself.

As a beginner, this seemed like a great way to start my work! No configuration! Just start coding!

CRA uses two popular packages to achieve this, webpack and Babel. webpack is a web bundler that optimizes all of the assets in your project, such as JavaScript, CSS, and images. Babel is a tool that allows you to use newer JavaScript features, even if some browsers don’t support them.

Both are good, but there are newer tools that can do the job better, specifically Vite and Speedy Web Compiler (SWC).

These new and improved alternatives are faster and easier to configure than webpack and Babel. This makes it easier to adjust the configuration which is difficult to do in create-react-app without ejecting.

To use them both when setting up a new React project you have to make sure you have Node version 12 or higher installed, then run the following command.

npm create vite

You’ll be asked to pick a name for your project. Once you do that, select React from the list of frameworks. After that, you can select either Javascript + SWC or Typescript + SWC

Then you’ll have to change directory cd into your project and run the following command;

npm i && npm run dev

This should run a development server for your site with the URL localhost:5173

And it’s as simple as that.

Using defaultProps for default values

TL;DR Use default function parameters instead.

Data can be passed to React components through something called props. These are added to a component just like attributes in an HTML element and can be used in a component’s definition by taking the relevant values from the prop object passed in as an argument.

// App.jsx
export default function App() {
  return <Card title="Hello" description="world" />
}

// Card.jsx
function Card(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      <p>{props.description}</p>
    </div>
  );
}

export default Card;

If a default value is ever required for a prop, the defaultProp property can be used:

// Card.jsx
function Card(props) {
  // ...
}

Card.defaultProps = {
  title: 'Default title',
  description: 'Desc',
};

export default Card;

With modern JavaScript, it is possible to destructure the props object and assign a default value to it all in the function argument.

// Card.jsx
function Card({title = "Default title", description= "Desc"}) {
  return (
    <div>
      <h1>{title}</h1>
      <p>{description}</p>
    </div>
  )
}

export default Card;

This is more favorable as the code that can be read by modern browsers without the need for extra transformation.

Unfortunately, defaultProps do require some transformation to be read by the browser since JSX (JavaScript XML) isn’t supported out of the box. This could potentially affect the performance of an application that is using a lot of defaultProps.

Don’t use propTypes

TL;DR Use TypeScript.

In React, the propTypes property can be used to check if a component is being passed the correct data type for its props. They allow you to specify the type of data that should be used for each prop such as a string, number, object, etc. They also allow you to specify if a prop is required or not.

This way, if a component is passed the wrong data type or if a required prop is not being provided, then React will throw an error.

// Card.jsx
import { PropTypes } from "prop-types";

function Card(props) {
  // ...
}

Card.propTypes = {
  title: PropTypes.string.isRequired,
  description: PropTypes.string,
};

export default Card;

TypeScript provides a level of type safety in data that’s being passed to components. So, sure, propTypes were a good idea back when I was starting. However, now that TypeScript has become the go-to solution for type safety, I would highly recommend using it over anything else.

// Card.tsx
interface CardProps {
  title: string,
  description?: string,
}

export default function Card(props: CardProps) {
  // ...
}

TypeScript is a programming language that builds on top of JavaScript by adding static type-checking. TypeScript provides a more powerful type system, that can catch more potential bugs and improves the development experience.

Using class components

TL;DR: Write components as functions

Class components in React are created using JavaScript classes. They have a more object-oriented structure and as well as a few additional features, like the ability to use the this keyword and lifecycle methods.

// Card.jsx
class Card extends React.Component {
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <p>{this.props.description}</p>
      </div>
    )
  }
}

export default Card;

I prefer writing components with classes over functions, but JavaScript classes are more difficult for beginners to understand and this can get very confusing. Instead, I’d recommend writing components as functions:

// Card.jsx
function Card(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      <p>{props.description}</p>
    </div>
  )
}

export default Card;

Function components are simply JavaScript functions that return JSX. They are much easier to read, and do not have additional features like the this keyword and lifecycle methods which make them more performant than class components.

Function components also have the advantage of using hooks. React Hooks allow you to use state and other React features without writing a class component, making your code more readable, maintainable and reusable.

Importing React unnecessarily

TL;DR: There’s no need to do it, unless you need hooks.

Since React 17 was released in 2020, it’s now unnecessary to import React at the top of your file whenever you create a component.

import React from 'react'; // Not needed!
export default function Card() {}

But we had to do that before React 17 because the JSX transformer (the thing that converts JSX into regular JavaScript) used a method called React.createElement that would only work when importing React. Since then, a new transformer has been release which can transform JSX without the createElement method.

You will still need to import React to use hooks, fragments, and any other functions or components you might need from the library:

import { useState } from 'react';

export default function Card() {
  const [count, setCount] = useState(0);
  // ...
}

Those were my early mistakes!

Maybe “mistake” is too harsh a word since some of the better practices came about later. Still, I see plenty of instances where the “old” way of doing something is still being actively used in projects and other tutorials.

To be honest, I probably made way more than five mistakes when getting started. Anytime you reach for a new tool it is going to be more like a learning journey to use it effectively, rather than flipping a switch. But these are the things I still carry with me years later!

If you’ve been using React for a while, what are some of the things you wish you knew before you started? It would be great to get a collection going to help others avoid the same struggles.


5 Mistakes I Made When Starting My First React Project originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/5-mistakes-starting-react/feed/ 0 377098
Different Ways to Write CSS in React https://css-tricks.com/different-ways-to-write-css-in-react/ https://css-tricks.com/different-ways-to-write-css-in-react/#comments Wed, 22 Jun 2022 14:24:37 +0000 https://css-tricks.com/?p=366452 We’re all familiar with the standard way of linking up a stylesheet to the <head> of an HTML doc, right? That’s just one of several ways we’re able to write CSS. But what does it look like to style things …


Different Ways to Write CSS in React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’re all familiar with the standard way of linking up a stylesheet to the <head> of an HTML doc, right? That’s just one of several ways we’re able to write CSS. But what does it look like to style things in a single-page application (SPA), say in a React project?

Turns out there are several ways to go about styling a React application. Some overlap with traditional styling, others not so much. But let’s count all the ways we can do it.

Importing external stylesheets

As the name suggests, React can import CSS files. The process is similar to how we link up CSS file in the HTML <head>:

  1. Create a new CSS file in your project directory.
  2. Write CSS.
  3. Import it into the React file.

Like this:

import "./style.css";

That usually goes at the top of the file where other imports happen:

import { React } from "react";
import "./Components/css/App.css";
function App() {
  return (
    <div className="main">
    </div>
  );
}
export default App;

In this example, a CSS file is imported into an App.js from the /Components/css folder.

Write inline styles

You may be used to hearing that inline styling isn’t all that great for maintainability and whatnot, but there are definitely situations (here’s one!) where it makes sense. And maintainability is less of an issue in React, as the CSS often already sits inside the same file anyway.

This is a super simple example of inline styling in React:

<div className="main" style={{color:"red"}}>

A better approach, though, is to use objects:

  1. First, create an object that contains styles for different elements.
  2. Then add it to an element using the style attribute and then select the property to style.

Let’s see that in context:

import { React } from "react";
function App() {
  const styles = {
    main: {
      backgroundColor: "#f1f1f1",
      width: "100%",
    },
    inputText: {
      padding: "10px",
      color: "red",
    },
  };
  return (
    <div className="main" style={styles.main}>
      <input type="text" style={styles.inputText}></input>
    </div>
  );
}
export default App;

This example contains a styles object containing two more objects, one for the .main class and the other for a text input, which contain style rules similar to what we’d expect to see in an external stylesheet. Those objects are then applied to the style attribute of elements that are in the returned markup.

Note that curly brackets are used when referencing styles rather than the quotation marks we’d normally use in plain HTML.

Use CSS Modules

CSS Modules… what the heck happened to those, right? They have the benefit of locally scoped variables and can be used right alongside React. But what are they, again, exactly?

Quoting the repo’s documentation:

CSS Modules works by compiling individual CSS files into both CSS and data. The CSS output is normal, global CSS, which can be injected directly into the browser or concatenated together and written to a file for production use. The data is used to map the human-readable names you’ve used in the files to the globally-safe output CSS.

In simpler terms, CSS Modules allows us to use the same class name in multiple files without clashes since each class name is given a unique programmatic name. This is especially useful in larger applications. Every class name is scoped locally to the specific component in which it is being imported.

A CSS Module stylesheet is similar to a regular stylesheet, only with a different extension (e.g. styles.module.css). Here’s how they’re set up:

  1. Create a file with .module.css as the extension.
  2. Import that module into the React app (like we saw earlier)
  3. Add a className to an element or component and reference the particular style from the imported styles.

Super simple example:

/* styles.module.css */
.font {
  color: #f00;
  font-size: 20px;
}

import { React } from "react";
import styles from "./styles.module.css";
function App() {
  return (
    <h1 className={styles.heading}>Hello World</h1>
  );
}
export default App;

Use styled-components

Have you used styled-components? It’s quite popular and allows you to build custom components using actual CSS in your JavaScript. A styled-component is basically a React component with — get ready for it — styles. Some of the features include unique class names, dynamic styling and better management of the CSS as each component has its own separate styles.

Install the styled-components npm package in the command line:

npm install styled-components

Next up, import it into the React app:

import styled from 'styled-components'

Create a component and assign a styled property to it. Note the use of template literals denoted by backticks in the Wrapper object:

import { React } from "react";
import styled from "styled-components";
function App() {
  const Wrapper = styled.div`
    width: 100%;
    height: 100px;
    background-color: red;
    display: block;
  `;
  return <Wrapper />;
}
export default App;

The above Wrapper component will be rendered as a div that contains those styles.

Conditional styling

One of the advantages of styled-components is that the components themselves are functional, as in you can use props within the CSS. This opens the door up to conditional statements and changing styles based on a state or prop.

Here’s a demo showing that off:

Here, we are manipulating the div’s display property on the display state. This state is controlled by a button that toggles the div’s state when clicked. This, in turn, toggles between the styles of two different states.

In inline if statements, we use a ? instead of the usual if/else syntax. The else part is after the semicolon. And remember to always call or use the state after it has been initialized. In that last demo, for example, the state should be above the Wrapper component’s styles.

Happy React styling!

That’s a wrap, folks! We looked at a handful of different ways to write styles in a React application. And it’s not like one is any better than the rest; the approach you use depends on the situation, of course. Hopefully now you’ve got a good understanding of them and know that you have a bunch of tools in your React styling arsenal.


Different Ways to Write CSS in React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/different-ways-to-write-css-in-react/feed/ 8 366452
Building Interoperable Web Components That Even Work With React https://css-tricks.com/building-interoperable-web-components-react/ https://css-tricks.com/building-interoperable-web-components-react/#comments Tue, 07 Jun 2022 13:57:57 +0000 https://css-tricks.com/?p=366222 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 …


Building Interoperable Web Components That Even Work With React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
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, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/building-interoperable-web-components-react/feed/ 3 366222
An Interactive Starry Backdrop for Content https://css-tricks.com/an-interactive-starry-backdrop-for-content/ https://css-tricks.com/an-interactive-starry-backdrop-for-content/#comments Wed, 18 May 2022 14:16:43 +0000 https://css-tricks.com/?p=365766 I was fortunate last year to get approached by Shawn Wang (swyx) about doing some work for Temporal. The idea was to cast my creative eye over what was on the site and come up with some


An Interactive Starry Backdrop for Content originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I was fortunate last year to get approached by Shawn Wang (swyx) about doing some work for Temporal. The idea was to cast my creative eye over what was on the site and come up with some ideas that would give the site a little “something” extra. This was quite a neat challenge as I consider myself more of a developer than a designer. But I love learning and leveling up the design side of my game.

One of the ideas I came up with was this interactive starry backdrop. You can see it working in this shared demo:

The neat thing about this design is that it’s built as a drop-in React component. And it’s super configurable in the sense that once you’ve put together the foundations for it, you can make it completely your own. Don’t want stars? Put something else in place. Don’t want randomly positioned particles? Place them in a constructed way. You have total control of what to bend it to your will.

So, let’s look at how we can create this drop-in component for your site! Today’s weapons of choice? React, GreenSock and HTML <canvas>. The React part is totally optional, of course, but, having this interactive backdrop as a drop-in component makes it something you can employ on other projects.

Let’s start by scaffolding a basic app

import React from 'https://cdn.skypack.dev/react'
import ReactDOM from 'https://cdn.skypack.dev/react-dom'
import gsap from 'https://cdn.skypack.dev/gsap'

const ROOT_NODE = document.querySelector('#app')

const Starscape = () => <h1>Cool Thingzzz!</h1>

const App = () => <Starscape/>

ReactDOM.render(<App/>, ROOT_NODE)

First thing we need to do is render a <canvas> element and grab a reference to it that we can use within React’s useEffect. For those not using React, store a reference to the <canvas> in a variable instead.

const Starscape = () => {
  const canvasRef = React.useRef(null)
  return <canvas ref={canvasRef} />
}

Our <canvas> is going to need some styles, too. For starters, we can make it so the canvas takes up the full viewport size and sits behind the content:

canvas {
  position: fixed;
  inset: 0;
  background: #262626;
  z-index: -1;
  height: 100vh;
  width: 100vw;
}

Cool! But not much to see yet.

We need stars in our sky

We’re going to “cheat” a little here. We aren’t going to draw the “classic” pointy star shape. We’re going to use circles of differing opacities and sizes.

Draw a circle on a <canvas> is a case of grabbing a context from the <canvas> and using the arc function. Let’s render a circle, err star, in the middle. We can do this within a React useEffect:

const Starscape = () => {
  const canvasRef = React.useRef(null)
  const contextRef = React.useRef(null)
  React.useEffect(() => {
    canvasRef.current.width = window.innerWidth
    canvasRef.current.height = window.innerHeight
    contextRef.current = canvasRef.current.getContext('2d')
    contextRef.current.fillStyle = 'yellow'
    contextRef.current.beginPath()
    contextRef.current.arc(
      window.innerWidth / 2, // X
      window.innerHeight / 2, // Y
      100, // Radius
      0, // Start Angle (Radians)
      Math.PI * 2 // End Angle (Radians)
    )
    contextRef.current.fill()
  }, [])
  return <canvas ref={canvasRef} />
}

So what we have is a big yellow circle:

This is a good start! The rest of our code will take place within this useEffect function. That’s why the React part is kinda optional. You can extract this code out and use it in whichever form you like.

We need to think about how we’re going to generate a bunch of “stars” and render them. Let’s create a LOAD function. This function is going to handle generating our stars as well as the general <canvas> setup. We can also move the sizing logic of the <canvas> sizing logic into this function:

const LOAD = () => {
  const VMIN = Math.min(window.innerHeight, window.innerWidth)
  const STAR_COUNT = Math.floor(VMIN * densityRatio)
  canvasRef.current.width = window.innerWidth
  canvasRef.current.height = window.innerHeight
  starsRef.current = new Array(STAR_COUNT).fill().map(() => ({
    x: gsap.utils.random(0, window.innerWidth, 1),
    y: gsap.utils.random(0, window.innerHeight, 1),
    size: gsap.utils.random(1, sizeLimit, 1),
    scale: 1,
    alpha: gsap.utils.random(0.1, defaultAlpha, 0.1),
  }))
}

Our stars are now an array of objects. And each star has properties that define their characteristics, including:

  • x: The star’s position on the x-axis
  • y: The star’s position on the y-axis
  • size: The star’s size, in pixels
  • scale: The star’s scale, which will come into play when we interact with the component
  • alpha: The star’s alpha value, or opacity, which will also come into play during interactions

We can use GreenSock’s random() method to generate some of these values. You may also be wondering where sizeLimit, defaultAlpha, and densityRatio came from. These are now props we can pass to the Starscape component. We’ve provided some default values for them:

const Starscape = ({ densityRatio = 0.5, sizeLimit = 5, defaultAlpha = 0.5 }) => {

A randomly generated star Object might look like this:

{
  "x": 1252,
  "y": 29,
  "size": 4,
  "scale": 1,
  "alpha": 0.5
}

But, we need to see these stars and we do that by rendering them. Let’s create a RENDER function. This function will loop over our stars and render each of them onto the <canvas> using the arc function:

const RENDER = () => {
  contextRef.current.clearRect(
    0,
    0,
    canvasRef.current.width,
    canvasRef.current.height
  )
  starsRef.current.forEach(star => {
    contextRef.current.fillStyle = `hsla(0, 100%, 100%, ${star.alpha})`
    contextRef.current.beginPath()
    contextRef.current.arc(star.x, star.y, star.size / 2, 0, Math.PI * 2)
    contextRef.current.fill()
  })
}

Now, we don’t need that clearRect function for our current implementation as we are only rendering once onto a blank <canvas>. But clearing the <canvas> before rendering anything isn’t a bad habit to get into, And it’s one we’ll need as we make our canvas interactive.

Consider this demo that shows the effect of not clearing between frames.

Our Starscape component is starting to take shape.

See the code
const Starscape = ({ densityRatio = 0.5, sizeLimit = 5, defaultAlpha = 0.5 }) => {
  const canvasRef = React.useRef(null)
  const contextRef = React.useRef(null)
  const starsRef = React.useRef(null)
  React.useEffect(() => {
    contextRef.current = canvasRef.current.getContext('2d')
    const LOAD = () => {
      const VMIN = Math.min(window.innerHeight, window.innerWidth)
      const STAR_COUNT = Math.floor(VMIN * densityRatio)
      canvasRef.current.width = window.innerWidth
      canvasRef.current.height = window.innerHeight
      starsRef.current = new Array(STAR_COUNT).fill().map(() => ({
        x: gsap.utils.random(0, window.innerWidth, 1),
        y: gsap.utils.random(0, window.innerHeight, 1),
        size: gsap.utils.random(1, sizeLimit, 1),
        scale: 1,
        alpha: gsap.utils.random(0.1, defaultAlpha, 0.1),
      }))
    }
    const RENDER = () => {
      contextRef.current.clearRect(
        0,
        0,
        canvasRef.current.width,
        canvasRef.current.height
      )
      starsRef.current.forEach(star => {
        contextRef.current.fillStyle = `hsla(0, 100%, 100%, ${star.alpha})`
        contextRef.current.beginPath()
        contextRef.current.arc(star.x, star.y, star.size / 2, 0, Math.PI * 2)
        contextRef.current.fill()
      })
    }
    LOAD()
    RENDER()
  }, [])
  return <canvas ref={canvasRef} />
}

Have a play around with the props in this demo to see how they affect the the way stars are rendered.

Before we go further, you may have noticed a quirk in the demo where resizing the viewport distorts the <canvas>. As a quick win, we can rerun our LOAD and RENDER functions on resize. In most cases, we’ll want to debounce this, too. We can add the following code into our useEffect call. Note how we also remove the event listener in the teardown.

// Naming things is hard...
const RUN = () => {
  LOAD()
  RENDER()
}

RUN()

// Set up event handling
window.addEventListener('resize', RUN)
return () => {
  window.removeEventListener('resize', RUN)
}

Cool. Now when we resize the viewport, we get a new generated starry.

Interacting with the starry backdrop

Now for the fun part! Let’s make this thing interactive.

The idea is that as we move our pointer around the screen, we detect the proximity of the stars to the mouse cursor. Depending on that proximity, the stars both brighten and scale up.

We’re going to need to add another event listener to pull this off. Let’s call this UPDATE. This will work out the distance between the pointer and each star, then tween each star’s scale and alpha values. To make sure those tweeted values are correct, we can use GreenSock’s mapRange() utility. In fact, inside our LOAD function, we can create references to some mapping functions as well as a size unit then share these between the functions if we need to.

Here’s our new LOAD function. Note the new props for scaleLimit and proximityRatio. They are used to limit the range of how big or small a star can get, plus the proximity at which to base that on.

const Starscape = ({
  densityRatio = 0.5,
  sizeLimit = 5,
  defaultAlpha = 0.5,
  scaleLimit = 2,
  proximityRatio = 0.1
}) => {
  const canvasRef = React.useRef(null)
  const contextRef = React.useRef(null)
  const starsRef = React.useRef(null)
  const vminRef = React.useRef(null)
  const scaleMapperRef = React.useRef(null)
  const alphaMapperRef = React.useRef(null)
  
  React.useEffect(() => {
    contextRef.current = canvasRef.current.getContext('2d')
    const LOAD = () => {
      vminRef.current = Math.min(window.innerHeight, window.innerWidth)
      const STAR_COUNT = Math.floor(vminRef.current * densityRatio)
      scaleMapperRef.current = gsap.utils.mapRange(
        0,
        vminRef.current * proximityRatio,
        scaleLimit,
        1
      );
      alphaMapperRef.current = gsap.utils.mapRange(
        0,
        vminRef.current * proximityRatio,
        1,
        defaultAlpha
      );
    canvasRef.current.width = window.innerWidth
    canvasRef.current.height = window.innerHeight
    starsRef.current = new Array(STAR_COUNT).fill().map(() => ({
      x: gsap.utils.random(0, window.innerWidth, 1),
      y: gsap.utils.random(0, window.innerHeight, 1),
      size: gsap.utils.random(1, sizeLimit, 1),
      scale: 1,
      alpha: gsap.utils.random(0.1, defaultAlpha, 0.1),
    }))
  }
}

And here’s our UPDATE function. It calculates the distance and generates an appropriate scale and alpha for a star:

const UPDATE = ({ x, y }) => {
  starsRef.current.forEach(STAR => {
    const DISTANCE = Math.sqrt(Math.pow(STAR.x - x, 2) + Math.pow(STAR.y - y, 2));
    gsap.to(STAR, {
      scale: scaleMapperRef.current(
        Math.min(DISTANCE, vminRef.current * proximityRatio)
      ),
      alpha: alphaMapperRef.current(
        Math.min(DISTANCE, vminRef.current * proximityRatio)
      )
    });
  })
};

But wait… it doesn’t do anything?

Well, it does. But, we haven’t set our component up to show updates. We need to render new frames as we interact. We can reach for requestAnimationFrame often. But, because we’re using GreenSock, we can make use of gsap.ticker. This is often referred to as “the heartbeat of the GSAP engine” and it’s is a good substitute for requestAnimationFrame.

To use it, we add the RENDER function to the ticker and make sure we remove it in the teardown. One of the neat things about using the ticker is that we can dictate the number of frames per second (fps). I like to go with a “cinematic” 24fps:

// Remove RUN
LOAD()
gsap.ticker.add(RENDER)
gsap.ticker.fps(24)

window.addEventListener('resize', LOAD)
document.addEventListener('pointermove', UPDATE)
return () => {
  window.removeEventListener('resize', LOAD)
  document.removeEventListener('pointermove', UPDATE)
  gsap.ticker.remove(RENDER)
}

Note how we’re now also running LOAD on resize. We also need to make sure our scale is being picked up in that RENDER function when using arc:

const RENDER = () => {
  contextRef.current.clearRect(
    0,
    0,
    canvasRef.current.width,
    canvasRef.current.height
  )
  starsRef.current.forEach(star => {
    contextRef.current.fillStyle = `hsla(0, 100%, 100%, ${star.alpha})`
    contextRef.current.beginPath()
    contextRef.current.arc(
      star.x,
      star.y,
      (star.size / 2) * star.scale,
      0,
      Math.PI * 2
    )
    contextRef.current.fill()
  })
}

It works! 🙌

It’s a very subtle effect. But, that’s intentional because, while it’s is super neat, we don’t want this sort of thing to distract from the actual content. I’d recommend playing with the props for the component to see different effects. It makes sense to set all the stars to low alpha by default too.

The following demo allows you to play with the different props. I’ve gone for some pretty standout defaults here for the sake of demonstration! But remember, this article is more about showing you the techniques so you can go off and make your own cool backdrops — while being mindful of how it interacts with content.

Refinements

There is one issue with our interactive starry backdrop. If the mouse cursor leaves the <canvas>, the stars stay bright and upscaled but we want them to return to their original state. To fix this, we can add an extra handler for pointerleave. When the pointer leaves, this tweens all of the stars down to scale 1 and the original alpha value set by defaultAlpha.

const EXIT = () => {
  gsap.to(starsRef.current, {
    scale: 1,
    alpha: defaultAlpha,
  })
}

// Set up event handling
window.addEventListener('resize', LOAD)
document.addEventListener('pointermove', UPDATE)
document.addEventListener('pointerleave', EXIT)
return () => {
  window.removeEventListener('resize', LOAD)
  document.removeEventListener('pointermove', UPDATE)
  document.removeEventListener('pointerleave', EXIT)
  gsap.ticker.remove(RENDER)
}

Neat! Now our stars scale back down and return to their previous alpha when the mouse cursor leaves the scene.

Bonus: Adding an Easter egg

Before we wrap up, let’s add a little Easter egg surprise to our interactive starry backdrop. Ever heard of the Konami Code? It’s a famous cheat code and a cool way to add an Easter egg to our component.

We can practically do anything with the backdrop once the code runs. Like, we could make all the stars pulse in a random way for example. Or they could come to life with additional colors? It’s an opportunity to get creative with things!

We’re going listen for keyboard events and detect whether the code gets entered. Let’s start by creating a variable for the code:

const KONAMI_CODE =
  'arrowup,arrowup,arrowdown,arrowdown,arrowleft,arrowright,arrowleft,arrowright,keyb,keya';

Then we create a second effect within our the starry backdrop. This is a good way to maintain a separation of concerns in that one effect handles all the rendering, and the other handles the Easter egg. Specifically, we’re listening for keyup events and check whether our input matches the code.

const codeRef = React.useRef([])
React.useEffect(() => {
  const handleCode = e => {
    codeRef.current = [...codeRef.current, e.code]
      .slice(
        codeRef.current.length > 9 ? codeRef.current.length - 9 : 0
      )
    if (codeRef.current.join(',').toLowerCase() === KONAMI_CODE) {
      // Party in here!!!
    }
  }
  window.addEventListener('keyup', handleCode)
  return () => {
    window.removeEventListener('keyup', handleCode)
  }
}, [])

We store the user input in an Array that we store inside a ref. Once we hit the party code, we can clear the Array and do whatever we want. For example, we may create a gsap.timeline that does something to our stars for a given amount of time. If this is the case, we don’t want to allow Konami code to input while the timeline is active. Instead, we can store the timeline in a ref and make another check before running the party code.

const partyRef = React.useRef(null)
const isPartying = () =>
  partyRef.current &&
  partyRef.current.progress() !== 0 &&
  partyRef.current.progress() !== 1;

For this example, I’ve created a little timeline that colors each star and moves it to a new position. This requires updating our LOAD and RENDER functions.

First, we need each star to now have its own hue, saturation and lightness:

// Generating stars! ⭐️
starsRef.current = new Array(STAR_COUNT).fill().map(() => ({
  hue: 0,
  saturation: 0,
  lightness: 100,
  x: gsap.utils.random(0, window.innerWidth, 1),
  y: gsap.utils.random(0, window.innerHeight, 1),
  size: gsap.utils.random(1, sizeLimit, 1),
  scale: 1,
  alpha: defaultAlpha
}));

Second, we need to take those new values into consideration when rendering takes place:

starsRef.current.forEach((star) => {
  contextRef.current.fillStyle = `hsla(
    ${star.hue},
    ${star.saturation}%,
    ${star.lightness}%,
    ${star.alpha}
  )`;
  contextRef.current.beginPath();
  contextRef.current.arc(
    star.x,
    star.y,
    (star.size / 2) * star.scale,
    0,
    Math.PI * 2
  );
  contextRef.current.fill();
});

And here’s the fun bit of code that moves all the stars around:

partyRef.current = gsap.timeline().to(starsRef.current, {
  scale: 1,
  alpha: defaultAlpha
});

const STAGGER = 0.01;

for (let s = 0; s < starsRef.current.length; s++) {
  partyRef.current
    .to(
    starsRef.current[s],
    {
      onStart: () => {
        gsap.set(starsRef.current[s], {
          hue: gsap.utils.random(0, 360),
          saturation: 80,
          lightness: 60,
          alpha: 1,
        })
      },
      onComplete: () => {
        gsap.set(starsRef.current[s], {
          saturation: 0,
          lightness: 100,
          alpha: defaultAlpha,
        })
      },
      x: gsap.utils.random(0, window.innerWidth),
      y: gsap.utils.random(0, window.innerHeight),
      duration: 0.3
    },
    s * STAGGER
  );
}

From there, we generate a new timeline and tween the values of each star. These new values get picked up by RENDER. We’re adding a stagger by positioning each tween in the timeline using GSAP’s position parameter.

That’s it!

That’s one way to make an interactive starry backdrop for your site. We combined GSAP and an HTML <canvas>, and even sprinkled in some React that makes it more configurable and reusable. We even dropped an Easter egg in there!

Where can you take this component from here? How might you use it on a site? The combination of GreenSock and <canvas> is a lot of fun and I’m looking forward to seeing what you make! Here are a couple more ideas to get your creative juices flowing…


An Interactive Starry Backdrop for Content originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/an-interactive-starry-backdrop-for-content/feed/ 2 365766
9 New React and JavaScript Links for February 2022 https://css-tricks.com/9-new-react-and-javascript-links-for-february-2022/ https://css-tricks.com/9-new-react-and-javascript-links-for-february-2022/#respond Fri, 11 Feb 2022 23:11:28 +0000 https://css-tricks.com/?p=363396 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 …


9 New React and JavaScript Links for February 2022 originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
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, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/9-new-react-and-javascript-links-for-february-2022/feed/ 0 https://www.youtube.com/embed/YX5yoApjI3M Turborepo Demo and Walkthrough (High-Performance Monorepos) nonadult 363396
User Registration and Auth Using Firebase and React https://css-tricks.com/user-registration-authentication-firebase-react/ https://css-tricks.com/user-registration-authentication-firebase-react/#comments Wed, 02 Feb 2022 15:42:43 +0000 https://css-tricks.com/?p=362521 The ability to identify users is vital for maintaining the security of any applications. Equally important is the code that’s written to manage user identities, particularly when it comes to avoiding loopholes for unauthorized access to data held by an …


User Registration and Auth Using Firebase and React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The ability to identify users is vital for maintaining the security of any applications. Equally important is the code that’s written to manage user identities, particularly when it comes to avoiding loopholes for unauthorized access to data held by an application. Writing authentication code without a framework or libraries available can take a ton of time to do right — not to mention the ongoing maintainance of that custom code.

This is where Firebase comes to the rescue. Its ready-to-use and intuitive methods make setting up effective user identity management on a site happen in no time. This tutorial will work us through on how to do that: implementing user registration, verification, and authentication.

Firebase v9 SDK introduces a new modular API surface, resulting in a change to several of its services, one of which is Firebase Authentication. This tutorial is current to the changes in v9.

To follow along with this tutorial, you should be familiar with React, React hooks, and Firebase version 8. You should also have a Google account and Node installed on your machine.

Table of Contents

Setting up Firebase

Before we start using Firebase for our registration and authentication requirements, we have to first set up our Firebase project and also the authentication method we’re using.

To add a project, make sure you are logged into your Google account, then navigate to the Firebase console and click on Add project. From there, give the project a name (I’m using “Firebase-user-reg-auth”) and we should be all set to continue.

You may be prompted to enable Google Analytics at some point. There’s no need for it for this tutorial, so feel free to skip that step.

Firebase has various authentication methods for both mobile and web, but before we start using any of them, we have to first enable it on the Firebase Authentication page. From the sidebar menu, click on the Authentication icon, then, on the next page, click on Get started.

We are going to use Email/Password authentication. Click on it and we will be prompted with a screen to enable it, which is exactly what we want to do.

Cloning and setting up the starter repo

I have already created a simple template we can use for this tutorial so that we can focus specifically on learning how to implement the functionalities. So what we need to do now is clone the GitHub repo.

Fire up your terminal. Here’s what we can run from the command line:

git clone -b starter https://github.com/Tammibriggs/Firebase_user_auth.git

cd Firebase_user_auth

npm install

I have also included Firebase version 9 in the dependency object of the package.json file. So, by running the npm install command, Firebase v9 — along with all other dependencies — will be installed.

With done that, let’s start the app with npm start!

Integrating Firebase into our React app

To integrate Firebase, we need to first get the web configuration object and then use it to initialize Firebase in our React app. Go over to the Firebase project page and we will see a set of options as icons like this:

Click on the web (</>) icon to configure our Firebase project for the web, and we will see a page like this:

Enter firebase-user-auth as the name of the web app. After that, click on the Register app button, which takes us to the next step where our firebaseConfig object is provided.

Copy the config to the clipboard as we will need it later on to initialize Firebase. Then click on the Continue to console button to complete the process.

Now, let’s initialize Firebase and Firebase Authentication so that we can start using them in our app. In the src directory of our React app, create a firebase.js file and add the following imports:

// src/firebase.js
import { initializeApp } from 'firebase/app'
import {getAuth} from 'firebase/auth'

Now, paste the config we copied earlier after the imports and add the following lines of code to initialize Firebase and Firebase Authentication.

// src/firebase.js
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)

export {auth}

Our firebase.js file should now look something like this:

// src.firebase.js
import { initializeApp } from "firebase/app"
import { getAuth } from "firebase/auth"

const firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "AUTH_DOMAIN",
  projectId: "PROJECT_ID",
  storageBucket: "STORAGE_BUCKET",
  messagingSenderId: "MESSAGING_SENDER_ID",
  appId: "APP_ID"
}

// Initialize Firebase and Firebase Authentication
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)
export {auth}

Next up, we’re going to cover how to use the ready-to-use functions provided by Firebase to add registration, email verification, and login functionality to the template we cloned.

Creating User Registration functionality

In Firebase version 9, we can build functionality for user registration with the createUserWithEmailAndPassword function. This function takes three arguments:

  • auth instance/service
  • email
  • password

Services are always passed as the first arguments in version 9. In our case, it’s the auth service.

To create this functionality, we will be working with the Register.js file in the src directory of our cloned template. What I did in this file is create three form fields — email, password, and confirm password — and input is controlled by the state. Now, let’s get to business.

Let’s start by adding a function that validates the password and confirm password inputs, checking if they are not empty and are the same: Add the following lines of code after the states in the Register component:

// src/Register.js
// ...

const validatePassword = () => {
  let isValid = true
  if (password !== '' && confirmPassword !== ''){
    if (password !== confirmPassword) {
      isValid = false
      setError('Passwords does not match')
    }
  }
  return isValid
}

// ...

In the above function, we return an isValid variable which can return either true or false based on the validity of the passwords. Later on, we will use the value of this variable to create a condition where the Firebase function responsible for registering users will only be invoked if isValid is true.

To create the registration functionality, let’s start by making the necessary imports to the Register.js file:

// src/Register.js
import {auth} from './firebase'
import {createUserWithEmailAndPassword} from 'firebase/auth'

Now, add the following lines of code after the validatePassword password function:

// src/Register.js
// ...

const register = e => {
  e.preventDefault()
  setError('')
  if(validatePassword()) {
    // Create a new user with email and password using firebase
      createUserWithEmailAndPassword(auth, email, password)
      .then((res) => {
          console.log(res.user)
        })
      .catch(err => setError(err.message))
  }
  setEmail('')
  setPassword('')
  setConfirmPassword('')
}

// ...

In the above function, we set a condition to call the createUserWithEmailAndPassword function only when the value returning from validatePassword is true.

For this to start working, let’s call the register function when the form is submitted. We can do this by adding an onSubmit event to the form. Modify the opening tag of the registration_form to look like this:

// src/Register.js
<form onSubmit={register} name='registration_form'>

With this, we can now register a new user on our site. To test this by going over to http://localhost:3000/register in the browser, filling in the form, then clicking on the Register button.

Showing a user registration form with fields to enter an email. a password, and password confirmation. A gray button labeled Register is below the three stacked fields.

After clicking the Register button, if we open the browser’s console we will see details of the newly registered user.

Managing User State with React Context API

Context API is a way to share data with components at any level of the React component tree without having to pass it down as props. Since a user might be required by a different component in the tree, using the Context API is great for managing the user state.

Before we start using the Context API, there are a few things we need to set up:

  • Create a context object using the createContext() method
  • Pass the components we want to share the user state with as children of Context.Provider
  • Pass the value we want the children/consuming component to access as props to Context.Provider

Let’s get to it. In the src directory, create an AuthContext.js file and add the following lines of code to it:

// src/AuthContext.js
import React, {useContext} from 'react'

const AuthContext = React.createContext()

export function AuthProvider({children, value}) {
  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuthValue(){
  return useContext(AuthContext)
}

In the above code, we created a context called AuthContext along with that we also created two other functions that will allow us to easily use the Context API which is AuthProvider and useAuthValue.

The AuthProvider function allows us to share the value of the user’s state to all the children of AuthContext.Provider while useAuthValue allows us to easily access the value passed to AuthContext.Provider.

Now, to provide the children and value props to AuthProvider, modify the App.js file to look something like this:

// src/App.js
// ...
import {useState} from 'react'
import {AuthProvider} from './AuthContext'

function App() {
  const [currentUser, setCurrentUser] = useState(null)

  return (
    <Router>
      <AuthProvider value={{currentUser}}>
        <Switch>
         ...
        </Switch>
      </AuthProvider>
    </Router>
  );
}

export default App;

Here, we’re wrapping AuthProvider around the components rendered by App. This way, the currentUser value supplied to AuthProvider will be available for use by all the components in our app except the App component.

That’s it as far as setting up the Context API! To use it, we have to import the useAuthValue function and invoke it in any of the child components of AuthProvider, like Login. The code looks something like this:

import { useAuthValue } from "./AuthContext"

function childOfAuthProvider(){
  const {currentUser} = useAuthValue()
  console.log(currentUser)

  return ...
}

Right now, currentUser will always be null because we are not setting its value to anything. To set its value, we need to first get the current user from Firebase which can be done either by using the auth instance that was initialized in our firebase.js file (auth.currentUser), or the onAuthStateChanged function, which actually happens to be the recommended way to get the current user. That way, we ensure that the Auth object isn’t in an intermediate state — such as initialization — when we get the current user.

In the App.js file, add a useEffect import along with useState and also add the following imports:

// src/App.js
import {useState, useEffect} from 'react'
import {auth} from './firebase'
import {onAuthStateChanged} from 'firebase/auth'

Now add the following line of code after the currentUser state in the App component:

// src/App.js
// ...

useEffect(() => {
  onAuthStateChanged(auth, (user) => {
    setCurrentUser(user)
   })
}, [])

// ...

In the above code, we are getting the current user and setting it in the state when the component renders. Now when we register a user the currentUser state will be set with an object containing the user’s info.

Send a verification email to a registered user

Once a user is registered, we want them to verify their email address before being able to access the homepage of our site. We can use the sendEmailVerification function for this. It takes only one argument which is the object of the currently registered user. When invoked, Firebase sends an email to the registered user’s email address with a link where the user can verify their email.

Let’s head over to the Register.js file and modify the Link and createUserWithEmailAndPassword import to look like this:

// src/Register.js
import {useHistory, Link} from 'react-router-dom'
import {createUserWithEmailAndPassword, sendEmailVerification} from 'firebase/auth'

In the above code, we have also imported the useHistory hook. This will help us access and manipulate the browser’s history which, in short, means we can use it to switch between pages in our app. But before we can use it we need to call it, so let’s add the following line of code after the error state:

// src/Register.js
// ...
const history = useHistory()

// ...

Now, modify the .then method of the createUserWithEmailAndPassword function to look like this:

// src/Register.js
// ...
.then(() => {
  sendEmailVerification(auth.currentUser)
  .then(() => {
    history.push('/verify-email')
  }).catch((err) => alert(err.message))
})
// ...

What’s happening here is that when a user registers a valid email address, they will be sent a verification email, then taken to the verify-email page.

There are several things we need to do on this page:

  • Display the user’s email after the part that says “A verification email has been sent to:”
  • Make the Resend Email button work
  • Create functionality for disabling the Resend Email button for 60 seconds after it is clicked
  • Take the user to their profile page once the email has been verified

We will start by displaying the registered user’s email. This calls for the use of the AuthContext we created earlier. In the VerifyEmail.js file, add the following import:

// src/VerifyEmail.js
import {useAuthValue} from './AuthContext'

Then, add the following code before the return statement in the VerifyEmail component:

// src/VerifyEmail.js
const {currentUser} = useAuthValue()

Now, to display the email, add the following code after the <br/> tag in the return statement.

// src/VerifyEmail.js
// ...
<span>{currentUser?.email}</span>
// ...

In the above code, we are using optional chaining to get the user’s email so that when the email is null our code will throw no errors.

Now, when we refresh the verify-email page, we should see the email of the registered user.

Let’s move to the next thing which is making the Resend Email button work. First, let’s make the necessary imports. Add the following imports to the VerifyEmail.js file:

// src/VerifyEmail.js
import {useState} from 'react'
import {auth} from './firebase'
import {sendEmailVerification} from 'firebase/auth'

Now, let’s add a state that will be responsible for disabling and enabling the Resend Email button based on whether or not the verification email has been sent. This code goes after currentUser in the VerifyEmail component:

// src/VerifyEmail.js
const [buttonDisabled, setButtonDisabled] = useState(false)

For the function that handles resending the verification email and disabling/enabling the button, we need this after the buttonDisabled state:

// src/VerifyEmail.js
// ...

const resendEmailVerification = () => {
  setButtonDisabled(true)
  sendEmailVerification(auth.currentUser)
  .then(() => {
    setButtonDisabled(false)
  }).catch((err) => {
    alert(err.message)
    setButtonDisabled(false)
  })
}

// ...

Next, in the return statement, modify the Resend Email button like this:

// ...
<button 
  onClick={resendEmailVerification}
  disabled={buttonDisabled}
  >Resend Email</button>
// ...

Now, if we go over to the verify-email page and click the button, another email will be sent to us. But there is a problem with how we created this functionality because if we try to click the button again in less than a minute, we get an error from Firebase saying we sent too many requests. This is because Firebase has a one minute interval before being able to send another email to the same address. That’s the net thing we need to address.

What we need to do is make the button stay disabled for 60 seconds (or more) after a verification email is sent. We can enhance the user experience a bit by displaying a countdown timer in Resend Email button to let the user know the button is only temporarily disabled.

In the VerifyEmail.js file, add a useEffect import:

import {useState, useEffect} from 'react'

Next, add the following after the buttonDisabled state:

// src/VerifyEmail.js
const [time, setTime] = useState(60)
const [timeActive, setTimeActive] = useState(false)

In the above code, we have created a time state which will be used for the 60-second countdown and also a timeActive state which will be used to control when the count down will start.

Add the following lines of code after the states we just created:

// src/VerifyEmail.js
// ...

useEffect(() => {
  let interval = null
  if(timeActive && time !== 0 ){
    interval = setInterval(() => {
      setTime((time) => time - 1)
    }, 1000)
  }else if(time === 0){
    setTimeActive(false)
    setTime(60)
    clearInterval(interval)
  }
  return () => clearInterval(interval);
}, [timeActive, time])

// ...

In the above code, we created a useEffect hook that only runs when the timeActive or time state changes. In this hook, we are decreasing the previous value of the time state by one every second using the setInterval method, then we are stopping the decrementing of the time state when its value equals zero.

Since the useEffect hook is dependent on the timeActive and time state, one of these states has to change before the time count down can start. Changing the time state is not an option because the countdown has to start only when a verification email has been sent. So, instead, we need to change the timeActive state.

In the resendEmailVerification function, modify the .then method of sendEmailVerification to look like this:

// src/VerifyEmail.js
// ...
.then(() => {
  setButtonDisabled(false)
  setTimeActive(true)
})
// ...

Now, when an email is sent, the timeActive state will change to true and the count down will start. In the code above we need to change how we are disabling the button because, when the count down is active, we want the disabled button.

We will do that shortly, but right now, let’s make the countdown timer visible to the user. Modify the Resend Email button to look like this:

// src/VerifyEmail.js
<button 
  onClick={resendEmailVerification}
  disabled={buttonDisabled}
>Resend Email {timeActive && time}</button>

To keep the button in a disabled state while the countdown is active, let’s modify the disabled attribute of the button to look like this:

disabled={timeActive}

With this, the button will be disabled for a minute when a verification email is sent. Now we can go ahead and remove the buttonDisabled state from our code.

Although this functionality works, there is still one problem with how we implemented it: when a user registers and is taken to the verify-email page when they have not received an email yet, they may try to click the Resend Email button, and if they do that in less than a minute, Firebase will error out again because we’ve made too many requests.

To fix this, we need to make the Resend Email button disabled for 60 seconds after an email is sent to the newly registered user. This means we need a way to change the timeActive state within the Register component. We can also use the Context API for this. It will allow us to globally manipulate and access the timeActive state.

Let’s make a few modifications to our code to make things work properly. In the VerifyEmail component, cut the timeActive state and paste it into the App component after the currentUser state.

// src/App.js
function App() {
  // ...
  const [timeActive, setTimeActive] = useState(false)

  // ...

Next, put timeActive and setTimeActive inside the object of AuthProvider value prop. It should look like this:

// src/App.js
// ...
<AuthProvider value={{currentUser, timeActive, setTimeActive}}>
// ...

Now we can access timeActive and setTimeActive within the children of AuthProvider. To fix the error in our code, go to the VerifyEmail.js file and de-structure both timeActive and setTimeActive from useAuthProvider:

// src/VerifyEmail.js
const {timeActive, setTimeActive} = useAuthValue()

Now, to change the timeActive state after a verification email has been sent to the registered user, add the following import in the Register.js file:

// src/Register.js
import {useAuthValue} from './AuthContext'

Next, de-structure setTimeActive from useAuthValue with this snippet among the other states in the Register component:

// src/Register.js
const {setTimeActive} = useAuthValue()

Finally, in the register function, set the timeActive state with the .then the method of sendEmailVerification:

// src/Register.js
// ...
.then(() => {
  setTimeActive(true)
  history.push('/verify-email')
})
// ...

With this, a user will be able to send a verification email without getting any errors from Firebase.

The last thing to fix concerning user verification is to take the user to their profile page after they have verified their email. To do this, we will use a reload function in the currentUser object. It allows us to reload the user object coming from Firebase, that way we will know when something has changed.

First, let’s make the needed imports. In the VerifyEmail.js file, let’s add this:

// src/VerifyEmail.js
import {useHistory} from 'react-router-dom'

We are importing useHistory so that we can use to navigate the user to the profile page. Next, add the following line of code after the states:

// src/VerifyEmail.js
const history = useHistory()

And, finally, add the following lines of code after the history variable:

// src/VerifyEmail.js
// ...

useEffect(() => {
  const interval = setInterval(() => {
    currentUser?.reload()
    .then(() => {
      if(currentUser?.emailVerified){
        clearInterval(interval)
        history.push('/')
      }
    })
    .catch((err) => {
      alert(err.message)
    })
  }, 1000)
}, [history, currentUser])

// ...

In the above code, we are running the reload function every one second until the user’s email has been verified, and, if it has, we are navigating the user to their profile page.

To test this, let’s verify our email by following the instructions in the email sent from Firebase. If all is good, we will be automatically taken to our profile page.

Right now the profile page is showing no user data and he Sign Out link does not work. That’s ur next task.

Working on the user profile page

Let’s start by displaying the Email and Email verified values. For this, we will make use of the currentUser state in AuthContext. What we need to do is import useAuthValue, de-structure currentUser from it, and then display the Email and Email verified value from the user object.

Here is what the Profile.js file should look like:

// src/Profile.js
import './profile.css'
import {useAuthValue} from './AuthContext'

function Profile() {
  const {currentUser} = useAuthValue()

  return (
    <div className='center'>
      <div className='profile'>
        <h1>Profile</h1>
        <p><strong>Email: </strong>{currentUser?.email}</p>
        <p>
          <strong>Email verified: </strong>
          {`${currentUser?.emailVerified}`}
        </p>
        <span>Sign Out</span>
      </div>
    </div>
  )
}

export default Profile

With this, the Email and Email verified value should now be displayed on our profile page.

To get the sign out functionality working, we will use the signOut function. It takes only one argument, which is the auth instance. So, in Profile.js. let’s add those imports.

// src/Profile.js
import { signOut } from 'firebase/auth' 
import { auth } from './firebase'

Now, in the return statement, modify the <span> that contains “Sign Out” so that is calls the signOut function when clicked:

// src/Profile.js
// ...
<span onClick={() => signOut(auth)}>Sign Out</span>
// ...

Creating a Private Route for the Profile component

Right now, even with an unverified email address, a user can access the profile page. We don’t want that. Unverified users should be redirected to the login page when they try to access the profile. This is where private routes come in.

In the src directory, let’s create a new PrivateRoute.js file and add the following code to it:

// src/PrivateRoute.js
import {Route, Redirect} from 'react-router-dom'
import {useAuthValue} from './AuthContext'

export default function PrivateRoute({component:Component, ...rest}) {
  const {currentUser} = useAuthValue()

  return (
    <Route
      {...rest}
      render={props => {
        return currentUser?.emailVerified ? <Component {...props} /> : <Redirect to='/login' />
    }}>
    </Route>
  )
}

This PrivateRoute is almost similar to using the Route. The difference is that we are using a render prop to redirect the user to the profile page if their email is unverified.

We want the profile page to be private, so well import PrivateRoute:

// src/App.js
import PrivateRoute from './PrivateRoute'

Then we can replace Route with PrivateRoute in the Profile component. The Profile route should now look like this:

// src/App.js
<PrivateRoute exact path="/" component={Profile} />

Nice! We have made the profile page accessible only to users with verified emails.

Creating login functionality

Since only users with verified emails can access their profile page when logged in with the signInWithEmailAndPassword function, we also need to check if their email has been verified and, if it is unverified, the user should be redirected to the verify-email page where the sixty-second countdown should also start.

These are the imports we need to add to the Login.js file:

import {signInWithEmailAndPassword, sendEmailVerification} from 'firebase/auth'
import {auth} from './firebase'
import {useHistory} from 'react-router-dom'
import {useAuthValue} from './AuthContext'

Next, add the following line of code among the states in the Login component.

// src/Login.js
const {setTimeActive} = useAuthValue()
const history = useHistory()

Then add the following function after the history variable:

// src/Login.js
// ...

const login = e => {
  e.preventDefault()
  signInWithEmailAndPassword(auth, email, password)
  .then(() => {
    if(!auth.currentUser.emailVerified) {
      sendEmailVerification(auth.currentUser)
      .then(() => {
        setTimeActive(true)
        history.push('/verify-email')
      })
    .catch(err => alert(err.message))
  }else{
    history.push('/')
  }
  })
  .catch(err => setError(err.message))
}

// ...

This logs in a user and then check if whether they are verified or not. If they are verified, we navigate them to their profile page. But if they are unverified, we send a verification email, then redirect them to the verify-email page.

All we need to do to make this work is call the login function when the form is submitted. So, let’s modify the opening tag of the login_form to this:

// src/Login.js
<form onSubmit={login} name='login_form'>

And, hey, we’re done!

Conclusion

In this tutorial, we have learned how to use version 9 of the Firebase Authentication to build a fully functioning user registration and authentication service in React. Is it super easy? No, there are a few thing we need to juggle. But is it a heck of a lot easier than building our own service from scratch? You bet it is! And that’s what I hope you got from reading this.

References


User Registration and Auth Using Firebase and React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/user-registration-authentication-firebase-react/feed/ 13 362521
Inertia https://css-tricks.com/inertia/ https://css-tricks.com/inertia/#comments Tue, 07 Dec 2021 16:20:21 +0000 https://css-tricks.com/?p=358321 I’ve always like Jeremy’s categorization of developer tools:

I’ve mentioned two categories of tools for web development. I still don’t know quite what to call these categories. Internal and external? Developer-facing and user-facing?

The first category covers things like


Inertia originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I’ve always like Jeremy’s categorization of developer tools:

I’ve mentioned two categories of tools for web development. I still don’t know quite what to call these categories. Internal and external? Developer-facing and user-facing?

The first category covers things like build tools, version control, transpilers, pre-processers, and linters. These are tools that live on your machine—or on the server—taking what you’ve written and transforming it into the raw materials of the web: HTML, CSS, and JavaScript.

The second category of tools are those that are made of the raw materials of the web: CSS frameworks and JavaScript libraries.

It’s a good way to think about things. There is nuance though, naturally. Sass is the first category since Sass never goes to users, it only makes CSS that goes to users. But it can still affect users because it could make CSS that is larger or smaller based on how you use it.

Jeremy mentions Svelte as a library where the goal is essentially compiling as much of itself away as it can before code goes to users. Some JavaScript is still there, but it doesn’t include the overhead of a developer-facing API. The nuance here is that Svelte can be used in such a way that all JavaScript is removed entirely. For example, SvelteKit can turn off it’s hydration entirely and do pre-rendering of pages, making a site that entirely JavaScript-free (or at least only opting in to it where you ask for it).

On React:

I know there are ways of getting React to behave more like a category one tool, but it is most definitely not the default behaviour. And default behaviour really, really matters. For React, the default behaviour is to assume all the code you write—and the tool you use to write it—will be sent over the wire to end users.

I think that’s fair to say, but it also seems like the story is slowly starting to change. I would think widespread usage is far off, but Server Components seem notable here because they are coming from the React team itself, just like SvelteKit is from the Svelte team itself.

And on Astro:

[…] unlike Svelte, Astro allows you to use the same syntax as the incumbent, React. So if you’ve learned React—because that’s what you needed to learn to get a job—you don’t have to learn a new syntax in order to use Astro.

I know you probably can’t take an existing React site and convert it to Astro with the flip of a switch, but at least there’s a clear upgrade path.

This isn’t just theoretically true, it’s demonstrably true!

I just converted our little serverless microsite from Gatsby to Astro. Gastby is React-based, so all the componentry is already built as React components. The Pull Request is messy but it’s here. I converted some of it to .astro files, but left a lot of the componentry largely untouched as .jsx React components. But React does not ship on the site to users. JavaScript is almost entirely removed from the site, save for some hand-written vanilla JavaScript for very light interactivity.

So there is some coin-flipping stuff happening here. Coin merging? Astro to me feels very much like a developer-facing tool. It helps me. It uses the Vite compiler and is super fast and pleasant to work with (Astro has rough edges, for sure, as it’s pre 1.0, but the DX is largely there). It scopes my styles. It lets me write SCSS. It lets me write components (in many different frameworks). But it also helps the user here. No more JavaScript bundle on the site at all.

I guess that means Astro doesn’t change the categories—it’s a developer-facing tool. It just happens to take what would be a user-facing tool (even Svelte) and makes them almost entirely developer-facing.


And just because I’ve had a couple of other Astro links burning a hole in my pocket, Flavio has a good intro tutorial and here’s Drew McLellan and Matthew Phillips chatting Astro on a recent Smashing Podcast.


And here’s Dave and I chatting about my recent little site-re-do in Astro:


Inertia originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/inertia/feed/ 1 358321
Easy Dark Mode (and Multiple Color Themes!) in React https://css-tricks.com/easy-dark-mode-and-multiple-color-themes-in-react/ https://css-tricks.com/easy-dark-mode-and-multiple-color-themes-in-react/#comments Thu, 11 Nov 2021 15:28:04 +0000 https://css-tricks.com/?p=355683 I was working on a large React application for a startup, and aside from just wanting some good strategies to keep our styles organized, I wanted to give this whole “dark mode” thing a shot. With the huge ecosystem around …


Easy Dark Mode (and Multiple Color Themes!) in React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I was working on a large React application for a startup, and aside from just wanting some good strategies to keep our styles organized, I wanted to give this whole “dark mode” thing a shot. With the huge ecosystem around React, you might think that there would be a go-to solution for style themes, but a little web searching shows that really isn’t the case.

There are plenty of different options out there, but many of them tie into very specific CSS strategies, like using CSS Modules, some form of CSS-in-JS, etc. I also found tools specific to certain frameworks, like Gatsby, but not a generic React project. What I was looking for was a basic system that’s easy to set up and work with without jumping through a ton of hoops; something fast, something easy to get a whole team of front-end and full-stack developers onboarded with quickly.

The existing solution I liked the best centered around using CSS variables and data attributes, found in this StackOverflow answer. But that also relied on some useRef stuff that felt hack-y. As they say in every infomercial ever, there’s got to be a better way!

Fortunately, there is. By combining that general CSS variable strategy with the beautiful useLocalStorage hook, we have a powerful, easy-to-use theming system. I’m going to walk through setting this thing up and running it, starting from a brand new React app. And if you stick around to the end, I also show you how to integrate it with react-scoped-css, which is what makes this my absolutely preferred way to work with CSS in React.

Project setup

Let’s pick this up at a very good place to start: the beginning.

This guide assumes a basic familiarity with CSS, JavaScript, and React.

First, make sure you have a recent version of Node and npm installed. Then navigate to whatever folder you want your project to live in, run git bash there (or your preferred command line tool), then run:

npx create-react-app easy-react-themes --template typescript

Swap out easy-react-themes with the name of your project, and feel free to leave off the --template typescript if you’d rather work in JavaScript. I happen to like TypeScript but it genuinely makes no difference for this guide, other than files ending in .ts/.tsx vs .js/.jsx.

Now we’ll open up our brand new project in a code editor. I’m using VS Code for this example, and if you are too, then you can run these commands:

cd easy-react-themes
code .
Not much to look at yet, but we’ll change that!

Running npm start next starts your development server, and produces this in a new browser window:

And, finally, go ahead and install the use-local-storage package with:

npm i use-local-storage

And that’s it for the initial setup of the project!

Code setup

Open the App.tsx file and get rid of the stuff we don’t need.

We want to go from this…
…to this.

Delete the entire content in App.css:

Woot! Now let’s create our themes! Open up the index.css file and add this to it:

:root {
  --background: white;
  --text-primary: black;
  --text-secondary: royalblue;
  --accent: purple;
}
[data-theme='dark'] {
  --background: black;
  --text-primary: white;
  --text-secondary: grey;
  --accent: darkred;
}

Here’s what we have so far:

See what we just did there? If you’re unfamiliar with CSS Custom Properties (as also known as CSS variables), they allow us to define a value to be used elsewhere in our stylesheets, with the pattern being --key: value. In this case, we’re only defining a few colors and applying them to the :root element so they can be used be used wherever else we need them across the whole React project.

The second part, starting with [data-theme='dark'], is where things get interesting. HTML (and JSX, which we’re using to create HTML in React) allows us to set completely arbitrary properties for our HTML elements with the data-* attribute. In this case, we are giving the outermost <div> element of our application a data-theme attribute and toggling its value between light and dark. When it’s dark, the CSS[data-theme='dark'] section overrides the variables we defined in the :root, so any styling which relies on those variables is toggled as well.

Let’s put that into practice. Back in App.tsx, let’s give React a way to track the theme state. We’d normally use something like useState for local state, or Redux for global state management, but we also want the user’s theme selection to stick around if they leave our app and come back later. While we could use Redux and redux-persist, that’s way overkill for our needs.

Instead, we’re using the useLocalStorage hook we installed earlier. It gives us a way to store things in local storage, as you might expect, but as a React hook, it maintains stateful knowledge of what it’s doing with localStorage, making our lives easy.

Some of you might be thinking “Oh no, what if the page renders before our JavaScript checks in with localStorage and we get the dreaded “flash of wrong theme?” But you don’t have to worry about that here since our React app is completely rendered client-side; the initial HTML file is basically a skeleton with a with a single <div> that React attaches the app to. All of the final HTML elements are generated by JavaScript after checking localStorage.

So, first, import the hook at the top of App.tsx with:

import useLocalStorage from 'use-local-storage'

Then, inside our App component, we use it with:

const defaultDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useLocalStorage('theme', defaultDark ? 'dark' : 'light');

This does a few things for us. First, we’re checking if the user has set a theme preference in their browser settings. Then we’re creating a stateful theme variable that is tied to localStorage and the setTheme function to update theme. useLocalStorage adds a key:value pair to localStorage if it doesn’t already exist, which defaults to theme: "light", unless our matchMedia check comes back as true, in which case it’s theme: "dark". That way, we’re gracefully handling both possibilities of keeping the theme settings for a returning user, or respecting their browser settings by default if we’re working with new users.

Next, we add a tiny bit of content to the App component so we have some elements to style, along with a button and function to actually allow us to toggle the theme.

The finished App.tsx file

The secret sauce is on line 14 where we’ve added data-theme={theme} to our top-level <div>. Now, by switching the value of theme, we are choosing whether or not to override the CSS variables in :root with the ones in the data-theme='dark' section of the index.css file.

The last thing we need to do is add some styling that uses those CSS variables we made earlier, and it’ll up and running! Open App.css and drop this CSS in there:

.App {
  color: var(--text-primary);
  background-color: var(--background);
  font-size: large;
  font-weight: bold;
  padding: 20px;
  height: calc(100vh - 40px);
  transition: all .5s;
}
button {
  color: var(--text-primary);
  background-color: var(--background);
  border: 2px var(--text-primary) solid;
  float: right;
  transition: all .5s;
}

Now the background and text for the main <div>, and the background, text, and outline of the <button> rely on the CSS variables. That means when the theme changes, everything that depends on those variables update as well. Also note that we added transition: all .5s to both the App and <button> for a smooth transition between color themes.

Now, head back to the browser that’s running the app, and here’s what we get:

Tada! Let’s add another component just to show how the system works if we’re building out a real app. We’ll add a /components folder in /src, put a /square folder in /components, and add a Square.tsx and square.css, like so:

Let’s import it back into App.tsx, like so:

Here’s what we have now as a result:

And there we go! Obviously, this is a pretty basic case where we’re only using a default (light) theme, and a secondary (dark) theme. But if your application calls for it, this system could be used to implement multiple theme options. Personally, I’m thinking of giving my next project options for light, dark, chocolate, and strawberry—go nuts!

Bonus: Integrating with React Scoped CSS:

Using React Scoped CSS is my favorite way to keep each component’s CSS encapsulated to prevent name collision messiness and unintended style inheritance. My previous go-to for this was CSS Modules, but that has the downside of making the in-browser DOM look like a robot wrote all of the class names… because that’s exactly the case. This lack of human-readability makes debugging far more annoying than it has to be. Enter React Scoped CSS. We get to keep writing CSS (or Sass) exactly the way we have been, and the output looks like a human wrote it.

Seeing as the the React Scoped CSS repo provides full and detailed installation instructions, I’ll merely summarize them here.

First, install and configure Create React App Configuration Override (CRACO) according to their instructions. Craco is a tool that lets us override some of the default webpack configuration that’s bundled into create-react-app (CRA). Normally, if you want to adjust webpack in a CRA project, you first have to “eject” the project, which is an irreversible operation, and makes you fully responsible for all of the dependencies that are normally handled for you. You usually want to avoid ejecting unless you really, really know what you’re doing and have a good reason to go down that road. Instead, CRACO let’s us make some minor adjustments to our webpack config without things getting messy.

Once that’s done, install the React Scoped CSS package:

npm i craco-plugin-scoped-css

(The README instructions use yarn for installation instead of npm, but either is fine.) Now that it’s installed, simply rename the CSS files by adding .scoped before the .css, like so:

app.css -> app.scoped.css

And we need to make sure we’re using a new name when importing that CSS into a component:

import './app.css'; -> import './app.scoped.css';

Now all of the CSS is encapsulated so that it only applies to the components they’re imported into. It works by using data-* properties, much like our theme system, so when a scoped CSS file is imported into a component, all of that component’s elements are labeled with a property, like data-v-46ef2374, and the styles from that file are wrapped so that they only apply to elements with that exact data property.

That’s all wonderful, but the little trick to making that work with this theming system is that we explicitly don’t want the CSS variables encapsulated; we want them applied to the whole project. So, we simply don’t change index.css to have scoped in it… in other words, we can leave that CSS file alone. That’s it! Now we have a powerful theming system working in harmony with scoped CSS— we’re living the dream!

Thank you so much taking a read through this guide, and if it helped you build something awesome, I would love to know about it!


Easy Dark Mode (and Multiple Color Themes!) in React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/easy-dark-mode-and-multiple-color-themes-in-react/feed/ 12 355683
How to Create a Contact Form With Next.js and Netlify https://css-tricks.com/how-to-create-a-contact-form-with-next-js-and-netlify/ https://css-tricks.com/how-to-create-a-contact-form-with-next-js-and-netlify/#comments Thu, 21 Oct 2021 14:26:03 +0000 https://css-tricks.com/?p=353128 We’re going to create a contact form with Next.js and Netlify that displays a confirmation screen and features enhanced spam detection.

Next.js is a powerful React framework for developing performant React applications that scale. By integrating a Next.js site with …


How to Create a Contact Form With Next.js and Netlify originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’re going to create a contact form with Next.js and Netlify that displays a confirmation screen and features enhanced spam detection.

Next.js is a powerful React framework for developing performant React applications that scale. By integrating a Next.js site with Netlify’s technology, we can quickly get a working contact form up and running without having to write any server-side code.

Not only is it a relatively fast process to set up forms to be processed by Netlify, but it’s also free to get started (with up to 100 free submissions/per site hosted on Netlify). Form submissions automatically go through Netlify’s built-in spam filter which uses Akismet and there are also options that can be configured to increase the level of spam detection.

Creating the contact form

Within the Next.js application we should create a ContactForm component to render the contact form inside of the contact page. If you’d like for this form to render at /contact, then the ContactForm component below with labels and input fields should be used within the pages/contact.js file.

const ContactForm = (
  <form
    name="contact-form"
    method="POST"
    action="contact/?success=true"
  >
    <label htmlFor="name">Name *</label>
    <input
      id="name"
      name="name"
      required
      type="text"
    />
    <label htmlFor="company">Company *</label>
    <input id="company" name="company" required type="text" />
    <label htmlFor="email">E-mail Address *</label>
    <input id="email" type="email" name="email" required />
    <label htmlFor="message">Message *</label>
    <textarea id="message" name="message" required></textarea>
    <button type="submit">Submit</button>
  </form>
);

The above markup is required to render a form with a field for Name, Company, Email address and message with a submit button. When submitting the form, based on the value of the form’s action, it should redirect to contact/?success=true from /contact. Right now there is not yet a difference between the page’s appearance with and without the success query parameter, but we will update that later.

Our Contact.js file looks like this so far:

import React from "react";
const ContactPage = () => {
 const ContactForm = (/* code in above code sample*/)
 
 return (
   <div>
     <h1>Contact Us</h1>
     {ContactForm}
   </div>
 );
};
 
export default ContactPage;

Now that we have the basic form set up, the real magic will happen after we add additional information for Netlify to auto-recognize the form during future site deployments. To accomplish this we should update the form to have the attribute data-netlify="true" and a hidden input field that contains the name of our contact form. In Netlify, once we navigate to our site in the dashboard and then click on the “forms” tab  we will be able to view our form responses based on the name that we’ve put in our hidden field. It’s important that if you have multiple forms within a site that they have unique names so that they are recorded properly in Netlify.

<form
  method="POST"
  name="contact-form"
  action="contact/?success=true"
  data-netlify="true"
>
<input type="hidden" name="form-name" value="contact-form" />

After successfully deploying the site to Netlify with the data-netlify attribute and the form-name field  then we can go to the deployed version of the site and fill out the form. Upon submitting the form and navigating to https://app.netlify.com/sites/site-name/forms (where site-name is the name of your site) then our most recent form submission should appear if we have successfully set up the form. 

Redirect to confirmation screen

In order to improve the user experience, we should add some logic to redirect to a confirmation screen on form submission when the URL changes to /contact/?success=true. There is also the option to redirect to an entirely different page as the action when the form is submitted but using query params we can achieve something similar with the Next Router. We can accomplish this by creating a new variable to determine if the confirmation screen or the form should be visible based on the query parameter. The next/router which is imported with import { useRouter } from "next/router"; can be used to retrieve the current query params. 

const router = useRouter();  
const confirmationScreenVisible = router.query?.success && router.query.success === "true";

In our case, the confirmation screen and form can never be visible at the same time; therefore, the following statement can be used to determine if the form is visible or not.

const formVisible = !confirmationScreenVisible; 

To give users the option to resubmit the form, we can add a button to the confirmation screen to reset the form by clearing the query params. Using router.replace (instead of router.push) not only updates the page but replaces the current page in the history to the version without query params. 

<button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button>

We can then conditionally render the form based on whether or not the form is visible with:

{formVisible ? ContactForm : ConfirmationMessage}

Putting it all together, we can use the following code to conditionally render the form based on the query params (which are updated when the form is submitted):

import React, { useState } from "react";
import { useRouter } from "next/router";
 
const ContactPage = () => {
 const [submitterName, setSubmitterName] = useState("");
 const router = useRouter();
 const confirmationScreenVisible =
   router.query?.success && router.query.success === "true";
 const formVisible = !confirmationScreenVisible;
 
 const ConfirmationMessage = (
   <React.Fragment>
     <p>
       Thank you for submitting this form. Someone should get back to you within 24-48 hours.
     </p>
 
     <button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button>
   </React.Fragment>
 );
 
 const ContactForm = (/* code in first code example */);
 
 return (
   <div>
     <h1>Contact Us</h1>
{formVisible ? ContactForm : ConfirmationMessage}
   </div>
 );
};
 
export default ContactPage;

Adding a hidden bot field

Now that the core functionality of our form is working, we can add additional spam detection to our form in addition to the base spam detection because Akismet is included with all Netlify Forms by default. We can enable this by adding data-netlify-honeypot="bot-field" to our form.

<form
  className="container"
  method="POST"
  name="contact-form"
  action="contact/?success=true"
  data-netlify="true"
  data-netlify-honeypot="bot-field"
>

We also need to create a new hidden paragraph that contains a label named bot-field that contains the input. This field is “visible” to bots, but not humans. When this honeypot form field is filled, Netlify detects a bot and then the submission is flagged as spam.

<p hidden>
  <label>
    Don’t fill this out: <input name="bot-field" />
  </label>
</p>

Further customizations

  • We could explore another spam prevention option that Netlify supports by adding reCAPTCHA 2 to a Netlify form.
  • We could update the form to allow uploaded files with input <input type="file">.
  • We could set up notifications for form submissions. That happens over at https://app.netlify.com/sites/[your-site-name]/settings/forms where we can include a custom subject field (which can be hidden) for email notifications.

Full code

The code for the full site code is available over at GitHub.

Bonus

The following code includes everything we covered as well as the logic for setting a custom subject line with what was submitted in the name field.

import React, { useState } from "react";
import { useRouter } from "next/router";
 
const ContactPage = () => {
 const [submitterName, setSubmitterName] = useState("");
 const router = useRouter();
 const confirmationScreenVisible =
   router.query?.success && router.query.success === "true";
 const formVisible = !confirmationScreenVisible;
 
 const ConfirmationMessage = (
   <React.Fragment>
     <p>
       Thank you for submitting this form. Someone should get back to you
       within 24-48 hours.
     </p>
 
     <button onClick={() => router.replace("/contact", undefined, { shallow: true })}> Submit Another Response </button>
   </React.Fragment>
 );
 
 const ContactForm = (
   <form
     className="container"
     method="POST"
     name="contact-form"
     action="contact/?success=true"
     data-netlify="true"
     data-netlify-honeypot="bot-field"
   >
     <input
       type="hidden"
       name="subject"
       value={`You've got mail from ${submitterName}`}
     />
     <input type="hidden" name="form-name" value="contact-form" />
     <p hidden>
       <label>
         Don’t fill this out: <input name="bot-field" />
       </label>
     </p>
 
     <label htmlFor="name">Name *</label>
     <input
       id="name"
       name="name"
       required
       onChange={(e) => setSubmitterName(e.target.value)}
       type="text"
     />
     <label htmlFor="company">Company *</label>
     <input id="company" name="company" required type="text" />
     <label htmlFor="email">E-mail Address *</label>
     <input id="email" type="email" name="email" required />
     <label htmlFor="message">Message *</label>
     <textarea id="message" name="message" required/>
     <button type="submit">Submit</button>
   </form>
 );
 
 return (
   <div>
     <h1>Contact Us</h1>
{formVisible ? ContactForm : ConfirmationMessage}
   </div>
 );
};
 
export default ContactPage;

How to Create a Contact Form With Next.js and Netlify originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-create-a-contact-form-with-next-js-and-netlify/feed/ 1 353128
A Themeable React Data Grid With Great UX-Focused Features https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/ https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/#comments Thu, 07 Oct 2021 14:35:28 +0000 https://css-tricks.com/?p=353242 (This is a sponsored post.)

KendoReact can save you boatloads of time because it offers pre-built componentry you can use in your app right away. They look nice, but more importantly, they are easily themeable, so they look however …


A Themeable React Data Grid With Great UX-Focused Features originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

KendoReact can save you boatloads of time because it offers pre-built componentry you can use in your app right away. They look nice, but more importantly, they are easily themeable, so they look however you need them to look. And I’d say the looks aren’t even the important part. There are lots of component libraries out there that focus on the visuals. These components tackle the hardest interactivity problems in UI/UX, and do it with grace, speed, and accessibility in mind.

Let’s take a look at their React Data Grid component.

The ol’ <table> element is the right tool for the job for data grids, but a table doesn’t offer most of the features that make for a good data browsing experience. If we use the KendoReact <Grid /> component (and friends), we get an absolute ton of extra features, any one of which is non-trivial to pull off nicely, and all together make for an extremely compelling solution. Let’s go through a list of what you get.

Sortable Columns

You’ll surely pick a default ordering for your data, but if any given row of data has things like ID’s, dates, or names, it’s perfectly likely that a user would want to sort the column by that data. Perhaps they want to view the oldest orders, or the orders of the highest total value. HTML does not help with ordering in tables, so this is table stakes (get it?!) for a JavaScript library for data grids, and it’s perfectly handled here.

Pagination and Limits

When you have any more than, say, a few dozen rows of data, it’s common that you want to paginate it. That way users don’t have to scroll as much, and equally importantly, it keeps the page fast by not making the DOM too enormous. One of the problems with pagination though is it makes things like sorting harder! You can’t just sort the 20 rows you can see, it is expected that the entire data set gets sorted. Of course that’s handled in KendoReact’s Data Grid component.

Or, if pagination isn’t your thing, the data grid offers virtualized scrolling — in both the column and row directions. That’s a nice touch as the data loads quickly for smooth, natural scrolling.

Expandable Rows

A data grid might have a bunch of data visible across the row itself, but there might be even more data that a user might want to dig out of an entry once they find it. Perhaps it is data that doesn’t need to be cross-referenced in the same way column data is. This can be tricky to pull off, because of the way table cells are laid out. The data is still associated with a single row, but you often need more room than the width of one cell offers. With the KendoReact Data Grid component, you can pass in a detail prop with an arbitrary React component to show when a row is expanded. Super flexible!

Notice how the expanded details can have their own <Grid /> inside!

Responsive Design

Perhaps the most notoriously difficult thing to pull off with <table> designs is how to display them on small screens. Zooming out isn’t very good UX, nor is collapsing the table into something non-table-like. The thing about data grids is that they are all different, and you’ll know data is most important to your users best. The KendoReact Data Grid component helps with this by making your data grid scrollable/swipeable, and also being able to lock columns to make sure they continue to be easy to find and cross-reference.

Filtering Data

This is perhaps my favorite feature just because of how UX-focused it is. Imagine you’re looking at a big data grid of orders, and you’re like “Let me see all orders from White Clover Markets.” With a filtering feature, perhaps you quickly type “clover” into the filter input, and viola, all those orders are right there. That’s extra tricky stuff when you’re also supporting ordering and pagination — so it’s great all these features work together.

Grouping Data

Now this feature actually blows my mind 🤯 a little bit. Filtering and sorting are both very useful, but in some cases, they leave a little bit to be desired. For example, it’s easy to filter too far too quickly, leaving the data you are looking at very limited. And with sorting, you might be trying to look at a subset of data as well, but it’s up to your brain to figure out where that data begins and ends. With grouping, you can tell the data grid to strongly group together things that are the most important to you, but then still leverage filtering and sorting on top of that. It instantly makes your data exploration easier and more useful.

Localization

This is where you can really tell KendoReact went full monty. It would be highly unfortunate to pick some kind of component library and then realize that you need localization and realize it wasn’t made to be a first-class citizen. You avoid all that with KendoReact, which you can see in this Data Grid component. In the demo, you can flip out English for Spanish with a simple dropdown and see all the dates localized. You pull off any sort of translation and localization with the <LocalizationProvider> and <IntlProvider>, both comfortable React concepts.

Exporting to PDF or Excel

Here’s a live demo of this:

C’mon now! That’s very cool.

That’s not all…

Go check out the docs for the React Data Grid. There are a bunch more features we didn’t even get to here (row pinning! cell editing!). And here’s something to ease your mind: this component, and all the KendoReact components, are keyboard friendly and meet Section 508 accessibility standards. That is no small feat. When components are this complex and involve this much interactivity, getting the accessibility right is tough. So not only are you getting good-looking components that work everywhere, you’re getting richly interactive components that deliver UX beyond what you might even think of, and it’s all done fast and accessiblty. That’s pretty unreal, really.


A Themeable React Data Grid With Great UX-Focused Features originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/a-themeable-react-data-grid-with-great-ux-focused-features/feed/ 3 https://css-tricks.com/wp-content/uploads/2021/10/CleanShot-2021-10-04-at-15.53.58.mp4 react Archives - CSS-Tricks nonadult 353242
Links on React and JavaScript II https://css-tricks.com/links-on-react-and-javascript-ii/ https://css-tricks.com/links-on-react-and-javascript-ii/#comments Fri, 01 Oct 2021 21:09:25 +0000 https://css-tricks.com/?p=352717
  • How To Use The Vite Build Tool with ReactVite is hot, in part, because it’s based on esbuild and wickedly fast. It’s from Evan You of Vue fame, but it’s not a Vue-specific tool. Here, NARUHODO covers how

  • Links on React and JavaScript II originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
  • How To Use The Vite Build Tool with ReactVite is hot, in part, because it’s based on esbuild and wickedly fast. It’s from Evan You of Vue fame, but it’s not a Vue-specific tool. Here, NARUHODO covers how to configure it to work with React.
  • React Architecture: How to Structure and Organize a React Application — Tania Rascia with “an opinionated guide” on project structure. Looks pretty nice to me. I like the @ import aliases. Looks like it would support a monorepo-type environment pretty well. I also like the distinction between global vs. resuable components (called just “components” here) and views vs. pages. I’d probably separate into three: Library Components (no global state, no queries/mutations, more design-y and intentionally reusable), Project Components (business logic, global state, not very reuable), and Pages (routing concerned).
  • What’s NOT new in React 18 — Benny Powers is a little salty about React’s lack of <web-components /> support. I agree it’s unfortunate, as web components do some things really well and React does some things really well and it would be nice to see them make buddies.
  • How React got Traction — A bit of irony when considering the above link… Shawn Wang and Pete Hunt talk on this podcast about the history of React and how it came to be so popular: “How React overcame its haters: by listening.”
  • Compound Components In React — Ichoku Chinonso covers this super useful pattern. Some components are built from a bucket of other little components (think Tabs, TabBar, Tab, TabPanels, TabPanel) and, with the Compound Component model, you get more flexibility, logical importing, and usage of the whole lot. I’m curious about the origins of this pattern. I know Ryan Florence was talking about it in 2017, and I first saw them via this Kent Dodds post. Googlin’ around, there are loads of random articles about it. Maybe it comes from deeper computer science concepts?
  • The Perils of Rehydration — Josh Comeau covers a bug that I’ve had to fight against multiple times in the last few weeks: React looking like it’s completely pooping the bed on constructing the DOM. Like elements that are clearly nested properly in the JSX appearing in parent elements, or like you’ve forgotten to close half your dang HTML elements and the browser is majorly confused. The problem comes from trying to do server side rendering (SSR) and client side rendering (CSR), which confuses the rehydration. The DOM from the SSR doesn’t match when CSR takes over. Fortunately, there is some fairly straightforward trickery to fix it.

  • Links on React and JavaScript II originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/links-on-react-and-javascript-ii/feed/ 1 352717
    Mars Theme: A Deep Look at Frontity’s Headless WordPress Theme https://css-tricks.com/mars-theme-a-deep-look-at-frontitys-headless-wordpress-theme/ https://css-tricks.com/mars-theme-a-deep-look-at-frontitys-headless-wordpress-theme/#comments Thu, 09 Sep 2021 14:31:18 +0000 https://css-tricks.com/?p=350070 Frontity is a framework for creating de-coupled (or "headless") WordPress sites. In this article, we dive into the process of spinning up such a site, with a detailed overview of the building blocks provided by Frontity and its flagship theme, Mars.


    Mars Theme: A Deep Look at Frontity’s Headless WordPress Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    This post was in progress before Automattic acquired Frontity and its entire team. According to Frontity’s founders, the framework will be transitioned into a community-led project and leave the project in “a stable, bug-free position” with documentation and features. Like other open-source community projects, Frontity will remain free as it has been, with opportunities to contribute to the project and make it an even better framework for decoupled WordPress. More detail is found in this FAQ page.

    In my previous article, we created a headless WordPress site with Frontity and briefly looked at its file structure. In this companion article, we will go into a deep dive of the @frontity/mars-theme package, or Mars Theme, with a step-by-step walkthrough on how to customize it to make our own. Not only is the Mars Theme a great starter, it’s Frontity’s default theme — sort of like WordPress Twenty Twenty-One or the like. That makes it a perfect starting point for us to get hands-on experience with Frontity and its features.

    Specifically, we will look at the fundamental parts of Frontity’s Mars Theme, including what they call “building blocks” as well as the different components that come with the package. We’ll cover what those components do, how they work, and finally, how styling works with examples.

    Ready? Let’s go!


    Frontity’s building blocks

    Let’s revisit the file structure of the Frontity project we made in the last article as that shows us exactly where to find Frontity’s building blocks, the frontity.settings.js, and package.json and packages/mars-theme folder. We covered these is great detail before but, in particular, the package.json file gives us a lot of information about the project, like the name, description, author, dependencies, etc. Here’s what that file includes:

    • frontity: this is the main package that includes all the methods used in Frontity app development. It’s also where the CLI lives.
    • @frontity/core: This is the most important package because it takes care of all the bundling, rendering, merging, transpiling, serving, etc. We don’t need to access to it in order to develop a Frontity app. The full list is captured in the Frontity docs.
    • @frontity/wp-source: This package connects to the WordPress REST API of our site and fetches all the data needed in the Mars Theme.
    • @frontity/tiny-router: This package handles window.history and helps us with routing.
    • @frontity/htmal2react: This package converts HTML to React, working with processors that match HTML portions while replacing them with React components.

    Frontity core, or @frontity/package (also referred as Frontity’s building block), is composed of useful React component libraries in its @frontity/components package, which exports helpful things like Link, Auto Prefetch, Image, Props, Iframe, Switch, and other functions, objects, etc., that can be directly imported into Frontity project components. A more detailed description of these components—including syntax info use cases—is in this package reference API.

    The Frontity docs provide a little more information on what happens when a Frontity project is started:

    When starting frontity, all the packages defined in frontity.settings.js are imported by @frontity/file-settings and the settings and exports from each package are merged by @frontity/core into a single store where you can access the state and actions of the different packages during development using @frontity/connect, the frontity state manager.

    Next up, we’re familiarizing ourselves with how these building blocks, utilities and exports are used in the Mars Theme package to create a functioning Frontity project with a headless WordPress endpoint.

    Section 1: Digging into the Mars Theme

    Before discussing styling and customizing let’s briefly familiarize ourselves with the Mars Theme (@frontity/mars-theme) file structure and how it is put together.

    #! frontity/mars-theme file structure
    packages/mars-theme/
    |__ src/
      |__ index.js
      |__ components/
         |__ list/
           |__ index.js
           |__ list-item.js
           |__ list.js
           |__ pagination.js
         |__ featured-media.js
         |__ header.js
         |__ index.js
         |__ link.js
         |__ loading.js
         |__ menu-icon.js
         |__ menu-model.js
         |__ menu.js
         |__ nav.js
         |__ page-error.js
         |__ post.js
         |__ title.js

    The Mars Theme has three important component files: /src/index.js file, src/list/index.js and src/components/index.js. Frontity’s documentation is a great resource for understanding the Mars Theme, with especially great detail on how different Mars Theme components are defined and connected together in a Frontity site. Let’s start familiarizing ourselves with the theme’s three most important components: Root, Theme and List.

    Theme Root component (/src/index.js)

    The src/index.js file, also known as the theme’s Root, is one of the most important Mars Theme components. The Root serves as an entry point that targets <div id="root"> in the site markup to inject the roots of all the installed packages required to run a Frontity project. A Frontity theme exports a root and other required packages in the DOM as shown in the following use case example from the Frontity documentation:

    <!-- /index.HTML (rendered by Frontity) -->
    <html>
      <head>...</head>
      <body>
        <div id="root">
          <MyAwesomeTheme />
          <ShareModal />
          <YetAnotherPackage />
        </div>
      </body>
    </html>

    This Frontity doc explains how Frontity extends its theme using extensibility patterns called Slot and Fill. An example of the Root component (/src/index.js) is taken from its Mars Theme package (@frontity/mars-theme).

    This is everything the package pulls in when initializing the Root component:

    // mars-theme/src/components/index.js
    import Theme from "./components";
    // import processor libraries
    import image from "@frontity/html2react/processors/image";
    import iframe from "@frontity/html2react/processors/iframe";
    import link from "@frontity/html2react/processors/link";
    
    const marsTheme = {
      // The name of the extension
      name: "@frontity/mars-theme",
      // The React components that will be rendered
      roots: {
        /** In Frontity, any package can add React components to the site.
          * We use roots for that, scoped to the `theme` namespace. */
        theme: Theme,
      },
      state: {
        /** State is where the packages store their default settings and other
          * relevant state. It is scoped to the `theme` namespace. */
        theme: {
          autoPrefetch: "in-view",
          menu: [],
          isMobileMenuOpen: false,
          featured: {
            showOnList: false,
            showOnPost: false,
          },
        },
      },
    
      /** Actions are functions that modify the state or deal with other parts of
        * Frontity-like libraries. */
      actions: {
        theme: {
          toggleMobileMenu: ({ state }) => {
            state.theme.isMobileMenuOpen = !state.theme.isMobileMenuOpen;
          },
          closeMobileMenu: ({ state }) => {
            state.theme.isMobileMenuOpen = false;
          },
        },
      },
      /** The libraries that the extension needs to create in order to work */
      libraries: {
        html2react: {
          /** Add a processor to `html2react` so it processes the `<img>` tags
            * and internal link inside the content HTML.
            * You can add your own processors too. */
          processors: [image, iframe, link],
        },
      },
    };
    
    export default marsTheme;

    The Mars Theme root component exports packages that includes any of the roots, fills, state, actions and libraries elements. More detailed information on Root can be found in this Frontity doc.

    Theme component (/src/components/index.js)

    The Frontity Theme component is its main root level component that is exported by the Theme namespace (lines 12-16, highlighted in the previous example. The Theme component is wrapped with the @frontity/connect function (line 51, highlighted below) which provides access to its state, actions and libraries props from the Root component instance and allows Theme component to read the state, manipulate through actions, or use code from other features packages in the libraries.

    // mars-theme/src/components/index.js
    import React from "react"
    // Modules from @emotion/core, @emotion/styled, css, @frontity/connect, react-helmet
    import { Global, css, connect, styled, Head } from "frontity";
    import Switch from "@frontity/components/switch";
    import Header from "./header";
    import List from "./list";
    import Post from "./post";
    import Loading from "./loading";
    import Title from "./title";
    import PageError from "./page-error";
    
    /** Theme is the root React component of our theme. The one we will export
     * in roots. */
    const Theme = ({ state }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
    
      return (
        <>
          {/* Add some metatags to the <head> of the HTML with react-helmet */}
          <Title />
          <Head>
            <meta name="description" content={state.frontity.description} />
            <html lang="en" />
          </Head>
    
          {/* Add some global styles for the whole site, like body or a's. 
          Not classes here because we use CSS-in-JS. Only global HTML tags. */}
          <Global styles={globalStyles} />
    
          {/* Render Header component. Add the header of the site. */}
          <HeadContainer>
            <Header />
          </HeadContainer>
    
          {/* Add the main section. It renders a different component depending
          on the type of URL we are in. */}
          <Main>
            <Switch>
              <Loading when={data.isFetching} />
              <List when={data.isArchive} />
              <Post when={data.isPostType} />
              <PageError when={data.isError} />
            </Switch>
          </Main>
        </>
      );
    };
    
    export default connect(Theme);
    
    {/* define Global styles and styled components used Theme component here */}
    const globalStyles = css`
      body {
        margin: 0;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
      }
      a,
      a:visited {
        color: inherit;
        text-decoration: none;
      }
    `;
    const HeadContainer = styled.div`
      // ...
    `;
    
    const Main = styled.div`
      // ...
    `;

    This example is pulled directly from the Mars Theme’s /src/components/index.js component, which we imported with connect from frontity (line 4, above). We are using state.source.get() to retrieve data to be rendered from the current path (lines 39-46, highlighted above); for example, List, Post and other components.

    Section 2: Working with the List component

    What we just looked at are the theme-level components in Frontity’s Mars Theme. You may have noticed that those components import additional components. Let’s look at a specific one of those, the List component.

    The List component is exported by src/components/list/index.js which uses @loadable/components to split the List component code in such a way that the component only loads when a user clicks a List view; otherwise it won’t render at all, like when a Post view is clicked instead.

    // src/components/list/index.js
    import { loadable } from "frontity";
    
    // Codesplit the list component so it's not included if the users
    // load a post directly.
    export default loadable(() => import("./list"));

    In this example, Frontity utilizes loadble functions (integrated from Loadable components) for code splitting which loads a component asynchronously and separates code into different bundles that are dynamically loaded at run time. Frontity’s core package API reference goes into much more detail.

    Displaying lists of posts

    To display a list of posts in an archive page, we first have to look Frontity src/components/list/list.js component. As the name suggests, the List component renders lists of posts using state.source.get(link) and its items field (lines 22-25, highlighted below).

    // src/components/list/list.js
    import { connect, styled, decode } from "frontity";
    import Item from "./list-item";
    import Pagination from "./pagination";
    
    const List = ({ state }) => {
      // Get the data of the current list.
      const data = state.source.get(state.router.link);
      return (
        <Container>
          {/* If the list is a taxonomy, we render a title. */}
          {data.isTaxonomy && (
            <Header>
              {data.taxonomy}: {state.source[data.taxonomy][data.id].name}
            </Header>
          )}
          {/* If the list is an author, we render a title. */}
          {data.isAuthor && (
            <Header>Author: {state.source.author[data.id].name}</Header>
          )}
          {/* Iterate over the items of the list. */}
          {data.items.map(({ type, id }) => {
            const item = state.source[type][id];
            // Render one Item component for each one.
            return <Item key={item.id} item={item} />;
          })}
          <Pagination />
        </Container>
      );
    };
    export default connect(List);

    In the code example above, the connect function is imported by frontity in line 2 and is wrapped around the exported connect(List) component in line 31 (the last line). Two other components, list-item.js and pagination.js are also imported. Let’s look at those next!

    Here’s what we have for list-item.js:

    // src/components/list/list-item.js
    import { connect, styled } from "frontity";
    import Link from "../link";
    import FeaturedMedia from "../featured-media";
    
    const Item = ({ state, item }) => {
      const author = state.source.author[item.author];
      const date = new Date(item.date);
      return (
        <article>
         {/* Rendering clickable post Title */}
          <Link link={item.link}>
            <Title dangerouslySetInnerHTML={{ __html: item.title.rendered }} />
          </Link>
          <div>
            {/* If the post has an author, we render a clickable author text. */}
            {author && (
              <StyledLink link={author.link}>
                <AuthorName>
                  By <b>{author.name}</b>
                </AuthorName>
              </StyledLink>
            )}
            {/* Rendering post date */}
            <PublishDate>
              {" "}
              on <b>{date.toDateString()}</b>
            </PublishDate>
          </div>
          {/* If the want to show featured media in the
           * list of featured posts, we render the media. */}
          {state.theme.featured.showOnList && (
            <FeaturedMedia id={item.featured_media} />
          )}
          {/* If the post has an excerpt (short summary text), we render it */}
          {item.excerpt && (
            <Excerpt dangerouslySetInnerHTML={{ __html: item.excerpt.rendered }} />
          )}
        </article>
      );
    };
    // Connect the Item to gain access to `state` as a prop
    export default connect(Item);

    The Item component renders the preview of a blog post with clickable post title (lines, 12-14, highlighted above), author name (lines 19-21, highlighted above) and published date (lines: 25-28, highlighted above) along with <FeaturedMedia /> which serves as a post’s optional featured image.

    Paginating a list of posts

    Let’s look at the Pagination component that was rendered earlier in the List component by the src/components/list/pagination/js that follows:

    // src/components/list/pagination.js
    import { useEffect } from "react";
    import { connect, styled } from "frontity";
    import Link from "../link";
    
    const Pagination = ({ state, actions }) => {
      // Get the total posts to be displayed based for the current link
      const { next, previous } = state.source.get(state.router.link);
      // Pre-fetch the the next page if it hasn't been fetched yet.
      useEffect(() => {
        if (next) actions.source.fetch(next);
      }, []);
      return (
        <div>
          {/* If there's a next page, render this link */}
          {next && (
            <Link link={next}>
              <Text>← Older posts</Text>
            </Link>
          )}
          {previous && next && " - "}
          {/* If there's a previous page, render this link */}
          {previous && (
            <Link link={previous}>
              <Text>Newer posts →</Text>
            </Link>
          )}
        </div>
      );
    };
    /**
     * Connect Pagination to global context to give it access to
     * `state`, `actions`, `libraries` via props
     */
    export default connect(Pagination);

    The Pagination component is used so that users can paginate between lists of posts — you know, like navigating forward from Page 1 to Page 2, or backward from Page 2 to Page 1. The state, actions, libraries props are provided by the global context that wraps and exports them with connect(Pagination).

    Displaying single posts

    The Post component displays both single posts and pages. Indeed, structurally both are the same except, in posts, we usually display meta data (author, date, categories etc). Meta data isn’t usually used in pages.

    In this Post component, conditional statements are rendered only if the post object contains data (i.e. data.isPost) and a featured image is selected in sate.theme.featured in the theme’s root component:

    // src/components/post.js
    import { useEffect } from "react";
    import { connect, styled } from "frontity";
    import Link from "./link";
    import List from "./list";
    import FeaturedMedia from "./featured-media";
    
    const Post = ({ state, actions, libraries }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
      // Get the data of the post.
      const post = state.source[data.type][data.id];
      // Get the data of the author.
      const author = state.source.author[post.author];
      // Get a human readable date.
      const date = new Date(post.date);
      // Get the html2react component.
      const Html2React = libraries.html2react.Component;
    
      useEffect(() => {
        actions.source.fetch("/");
        {/* Preloading the list component which runs only on mount */}
        List.preload();
      }, []);
    
      // Load the post, but only if the data is ready.
      return data.isReady ? (
        <Container>
          <div>
            <Title dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
            {/* Only display author and date on posts */}
            {data.isPost && (
              <div>
                {author && (
                  <StyledLink link={author.link}>
                    <Author>
                      By <b>{author.name}</b>
                    </Author>
                  </StyledLink>
                )}
                <DateWrapper>
                  {" "}
                  on <b>{date.toDateString()}</b>
                </DateWrapper>
              </div>
            )}
          </div>
          {/* Look at the settings to see if we should include the featured image */}
          {state.theme.featured.showOnPost && (
            <FeaturedMedia id={post.featured_media} />
          )}
          {/* Render the content using the Html2React component so the HTML is processed
           by the processors we included in the libraries.html2react.processors array. */}
          <Content>
            <Html2React html={post.content.rendered} />
          </Content>
        </Container>
      ) : null;
    };
    {/* Connect Post to global context to gain access to `state` as a prop. */} 
    export default connect(Post);

    We just saw how important the List component is when it comes to displaying a group of posts. It’s what we might correlate to the markup we generally use when working with the WordPress loop for archive pages, latest posts feeds, and other post lists.

    There are a few more components worth looking at before we get into Mars Theme styling.

    The following MarsLink component comes from src/components/link.js, which is a wrapper on top of the {@link Link} component. It accepts the same props as the {@link Link} component.

    // src/components/link.js
    import { connect, useConnect } from "frontity";
    import Link from "@frontity/components/link";
    
    const MarsLink = ({ children, ...props }) => {
      const { state, actions } = useConnect();
    
      /** A handler that closes the mobile menu when a link is clicked. */
      const onClick = () => {
        if (state.theme.isMobileMenuOpen) {
          actions.theme.closeMobileMenu();
        }
      };
    
      return (
        <Link {...props} onClick={onClick} className={className}>
          {children}
        </Link>
      );
    };
    // Connect the Item to gain access to `state` as a prop
    export default connect(MarsLink, { injectProps: false });

    As explained in this tutorial, the Link component provides a link attribute that takes a target URL as its value. Quoting from the doc: it outputs an <a> element into the resulting HTML, but without forcing a page reload which is what would occur if you simply added an <a> element instead of using the Link component.

    Frontity menu (src/components/nav.js)

    Earlier, we defined values for menu items in the frontity.settings.js file. In the Nav component (located in src/components/nav/js) those menu item values are iterated over, match their page url, and display the component inside the Header component.

    // src/components/nav.js
    import { connect, styled } from "frontity";
    import Link from "./link";
    
    const Nav = ({ state }) => (
      <NavContainer>
        // Iterate over the menu exported from state.theme and menu items value set in frontity.setting.js
        {state.theme.menu.map(([name, link]) => {
          // Check if the link matched the current page url
          const isCurrentPage = state.router.link === link;
          return (
            <NavItem key={name}>
              {/* If link URL is the current page, add `aria-current` for a11y */}
              <Link link={link} aria-current={isCurrentPage ? "page" : undefined}>
                {name}
              </Link>
            </NavItem>
          );
        })}
      </NavContainer>
    );
    // Connect the Item to gain access to `state` as a prop
    export default connect(Nav);

    The Mars Theme provides two additional menu components — menu.js and menu-modal.js — for mobile device views which, like nav.js, are available from the Mars Theme GitHub repository.

    In Frontity, featured media items values are defined in the Root component ‘s theme.state.featured line that we discussed earlier. Its full code is available in the /src/components/featured-media.js component file.

    Now that we’re more familiar with the Mars Theme, as well as its building blocks, components, and functions, we can move into the different approaches that are available for styling the Mars Theme front-end.

    As we move along, you may find this Frontity doc a good reference for the various styling approaches we cover.

    Section 4: How to style a Frontity project

    For those of us coming from WordPress, styling in Frontity looks and feels different than the various approaches for overriding styles in a typical WordPress theme.

    First off, Frontity provides us with reusable components made with with styled-components, and Emotion, a CSS library for styling components in JavaScript, right out of the box. Emotion is popular with React and JavaScript developers, but not so much in the WordPress community based on what I’ve seen. CSS-Tricks has covered CSS-in-JS in great detail including how it compares with other styling, and this video provides background information about the library. So, knowing that both styled-components and Emotion are available and ready to use is nice context as we get started.

    Frontity’s documentation has great learning resources for styling frontity components as well as set-by-step guidance for customizing Frontity theme styles.

    I am new to the CSS-in-JS world, except for some general reading on it here and there. I was exposed to CSS-in-JS styling in a Gatsby project, but Gatsby provides a bunch of other styling options that aren’t readily available in Frontity or the Mars Theme. That said, I feel I was able to get around that lack of experience, and what I learned from my discovery work is how I’m going to frame things.

    So, with that, we are going to visit a few styling examples, referencing Frontity’s styling documentation as we go in order to familiarize ourselves with even more information.

    Using styled-components

    As the name suggests, we need a component in order to style it. So, first, let’s create a styled-component using Emotion’s styled function.

    Let’s say we want to style a reusable <Button /> component that’s used throughout our Frontity project. First, we should create a <Button /> component (where its div tag is appended with a dot) and then call the component with a template literal for string styles.

    // Creating Button styled component
    import { styled } from "frontity"
    
    const Button = styled.div`
      background: lightblue;
      width: 100%;
      text-align: center;
      color: white;
    `

    Now this <Button /> component is available to import in other components. Let’s look specifically at the Mars Theme <Header /> component to see how the styled-component is used in practice.

    // mars-theme/src/components/header.js
    import { connect, styled } from "frontity";
    import Link from "./link";
    import MobileMenu from "./menu";
    
    const Header = ({ state }) => {
      return (
        <>
          <Container> // This component is defined later
            <StyledLink link="/"> // This component is defined later
              <Title>{state.frontity.title}</Title> // This component is defined later
            </StyledLink>
            // ...
          </Container>
        </>
      );
    };
    
    // Connect the Header component to get access to the `state` in its `props`
    export default connect(Header);
    
    // Defining the Container component that is a div with these styles
    const Container = styled.div` 
      width: 848px;
      max-width: 100%;
      box-sizing: border-box;
      padding: 24px;
      color: #fff;
      display: flex;
      flex-direction: column;
      justify-content: space-around;
    `;
    // Defining Title component that is h2 with these styles 
    const Title = styled.h2`
      margin: 0;
      margin-bottom: 16px;
    `;
    // Defining StyledLink component that is a third-party Link component
    const StyledLink = styled(Link)`
      text-decoration: none;
    `;

    In the above code example, the <StyledLink /> component (lines 39-41, highlighted above) is used to style another component, <Link />. Similarly. the <Container /> and <Title /> styled-components are used to style the site title and the site’s main container width.

    The Emotion docs describe how a styled component can be used as long as it accepts className props. This is a useful styling tool that can be extended using a variable as shown in the following example below from Frontity’s documentation:

    // mars-theme/src/components/header.js 
    // ...
    // We create a variable to use later as an example
    Const LinkColor = "green";
    
    // ... 
    
    // Defining StyledLink component that is a third-party Link component
    const StyledLink = styled(Link)`
      text-decoration: none;
      Background-color: ${linkColor};
    `;

    The styled component above is used extensively in the Mars Theme. But before we go further, let’s look at using a CSS prop to style components.

    Using a CSS prop

    The css prop is available as a template literal for inline styling from the Frontity core package. It is similar to styled-components, except css does not return a React component but rather a special object that can be passed to a component through the css prop.

    /* Using as CSS prop */
    import { css } from "frontity";
    
    const PinkButton = () => (
      <div css={css`background: pink`}>
        My Pink Button
      </div>
    );

    See that? We can style a component inline using the css prop on a component. Additional use case examples are available in the Emotion docs.

    Using the <Global /> component

    <Global /> is a React component that allows to us create site-wide general styles, though Frontity does not optimize it for performance. Global styles should be added to the <Theme /> root component.

    // packages/mars-theme/src/components/index.js
    // ...
    
    import { Global, css, styled } from "frontity";
    import Title from "./title";
    import Header from "./header";
    // ...
    
    // Theme root
    const Theme = ({ state }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
    
      return (
       <>
         {/* Add some metatags to the <head> of the HTML. */}
          <Title />
            // ...
          {/* Add global styles */}
          <Global styles={globalStyles} />
          {/* Add the header of the site. */}
          <HeadContainer>
            <Header />
          </HeadContainer>
            // ...
       </>
      );
     };
    
    export default connect(Theme);
    
    const globalStyles = css`
      body {
        margin: 0;
        font-family: -apple-system, "Helvetica Neue", Helvetica, sans-serif;
      }
      a,
      a:visited {
        color: inherit;
        text-decoration: none;
      }
    `;
    
    const HeadContainer = styled.div`
      // ...
    `;

    The <Global /> component has a style attribute that takes a css function as its value and consists of standard CSS inside back ticks (lines 35-45, highlighted above) as template literals. Frontity recommends using global styles for globally-used HTML tags, like <html>, <body>, <a>, and <img>.

    Additional CSS styling options — including a dynamic CSS prop and React style props — are described in this Frontity guide to styling.

    Resources for customizing a Frontity theme

    I did a lot of research heading into my Mars Theme project and thought I’d share some of the more useful resources I found for styling Frontity themes:

    • Official Frontity themes. In addition to the default Mars Theme, Frontity has a ready-to-use package that ports the default WordPress Twenty Twenty theme in its entirety to a Frontity project. You will notice in the next section that my style customizations were inspired by this great learning resource.
    • Community themes. At this time of this writing, there are a grand total of nine Frontity community members who contributed fully functional theme packages. Those themes can be cloned into your own project and customized according to your needs. Likewise, many of the sites included in the Frontity showcase have GitHub repository links, and just as we can copy or pick up design tips from WordPress themes, we can use these resources to customize our own Frontity theme by referencing these packages.
    • Creating your own theme from scratch. The Frontity tutorial site has an excellent step-by-step guide to create your own fully working and functional theme package from scratch. Although it’s a little time consuming to go through it all, it is the best approach to fully understand a Frontity site project.

    Now that we have covered the more commonly used Frontity styling techniques, let’s apply what we’ve learned to start customizing our Mars Theme project.

    Section 5: Customizing the Frontity Mars Theme

    I’m going to share one of my working Frontity projects, where I took the Mars Theme as a base and modified it with the resources we’ve covered so far. Because this is my learning playground, I took time to learn from Frontity default themes, community themes and Frontity showcase sites.

    So here are examples of how I customized Frontity’s Mars Theme for my headless WordPress site project.

    Changing the theme package name

    First, I wanted to change the @frontity/mars-theme package name to something different. It’s a good idea to change the package name and make sure all of the dependencies in the package file are up to date. Luis Herrera outlines the required steps for renaming the Mars Theme package in this frontity community forum, which I used as a reference to go from @fontity/mars-theme package to @frontity/labre-theme.

    So, open up the package.json file and change the name property on line 2. This is the name of the package that gets used throughout the project.

    Screenshot of the package.json file open in VS Code. The left panel shows the files and the right panel displays the code.
    I renamed my project from mars-theme to labre-theme in my package.json file,.

    We should also update the name of the project folder while we’re at it. We can do that on line 25. I changed mine from ./package/mars-theme to ./package/labre-theme. Now, the theme package is properly listed as a dependency and will be imported to the project.

    Our frontity-settings.js file needs to reflect the name change. So, let’s open that up and:

    • rename the package name on line 13 (I changed mine from @frontity/mars-theme to @frontity/labre-theme), and
    • rename the name on line 3 (I changed mine from mars-demo to labre-demo).
    // @frontity-settings.js
    const settings = {
      "name": "labre-demo",
      "state": {
        "frontity": {
          "url": "http://frontitytest.local",
          "title": "Frontity Demo Blog",
          "description": "Exploring Frontity as Headless WordPress"
        }
      },
      "packages": [
        {
          "name": "@frontity/labre-theme",
          "state": {
            "theme": {
              "menu": [
                ["Home", "/"],
                ["Block", "/category/block/"],
                ["Classic", "/category/classic/"],
                ["Alignments", "/tag/alignment-2/"],
                ["About", "/about/"]
              ],
     // ...

    Next up, we want to re-initialize the project with these changes. We should delete the node_modules folder with rm -rf node_modules in a terminal and reinstall the npm package with yarn install. Once the npm package is reinstalled, everything gets properly linked internally and our Frontity project runs just fine without any errors.

    Refactoring navigation with dynamic menu fetching

    As we discussed earlier, Frontity menu items are either hard-coded in the frontity.setting.js file or in index.js component that’s stored in the Frontity state. However, WordPress can dynamically fetch the Frontity menu. In fact, Frontity just so happens to have a YouTube video on the subject. Let me break down the key steps here.

    The first step is to install the WP-REST-API V2 Menus plugin in WordPress. The plugin is freely available in the WordPress Plugin Directory, which means you can find it and activate it directly from the WordPress admin.

    Why do we need this plugin? It extends the new routes to all the registered WordPress menus to the REST API (e.g. /menus/v1/menus/<slug>).

    If we check our project site at /wp-json/menu/v1/menus, it should display our selected menu items in the JSON. We can get the menu items with the menu item’s slug property.

    Next, let’s use the menuHandler function from the tutorial. Create a new menu-handler.js file at src/components/handler/menu-handler.js and paste in the following code:

    // src/components/handler/menu-handler.js
    const menuHandler = {
      name: "menus",
      priority: 10,
      pattern: "/menu/:slug",
      func: async ({ link, params, state, libraries }) => {
        console.log("PARAMS:", params);
        const { slug } = params;
    
        // Fetch the menu data from the endpoint
        const response = await libraries.source.api.get({
          endpoint: `/menus/v1/menus/${slug}`,
        });
    
        // Parse the JSON to get the object
        const menuData = await response.json();
    
        // Add the menu items to source.data
        const menu = state.source.data[link];
        console.log(link);
        Object.assign(menu, {
          items: menuData.items,
          isMenu: true,
        });
      },
    };
    
    export default menuHandler;

    This menuHandler function is only executed if the pattern value (i.e. /menu/:slug) matches. Now let’s update our /src/index.js root component so it imports the handler:

    // src/index.js
    import Theme from "./components";
    import image from "@frontity/html2react/processors/image";
    import iframe from "@frontity/html2react/processors/iframe";
    import link from "@frontity/html2react/processors/link";
    import menuHandler from "./components/handlers/menu-handler";
    
    const labreTheme = {
      // ...
      state: {
        theme: {
          autoPrefetch: "in-view",
          menu: [],
          {/* Add menuURL property with menu slug as its value */}
          menuUrl: "primary-menu",
          isMobileMenuOpen: false,
          // ...
        },
      },
    
      /** Actions are functions that modify the state or deal with other parts of
        * Frontity-like libraries */
      actions: {
        theme: {
          toggleMobileMenu: ({ state }) => {
            state.theme.isMobileMenuOpen = !state.theme.isMobileMenuOpen;
          },
          closeMobileMenu: ({ state }) => {
            state.theme.isMobileMenuOpen = false;
          },
          {/* Added before SSR action */}
          beforeSSR: async ({ state, actions }) => {
            await actions.source.fetch(`/menu/${state.theme.menuUrl}/`);
          },
        },
      },
      libraries: {
        // ...
        {/* Added menuHandler source */}
        source: {
          handlers: [menuHandler],
        },
      },
    };
    
    export default labreTheme;

    Add an array of handlers under the source property and fetch data before the beforeSSR function. It does not fetch but does match the menu-handler slug, which means menuHandler() is executed. That puts the menu items into state and they become available to manipulate.

    Please note that we have added a new menuUrl property here (line 15 above) which can be used as a variable at our endpoint in handlers, as well as the nav.js component. Then, changing the value of menuUrl in the index.js root component, we could display another menu.

    Let’s get this data into our theme through state and map with menu-items to display on the site.

    // src/components/nav.js
    import { connect, styled } from "frontity";
    import Link from "./link";
    
    /** Navigation Component. It renders the navigation links */
    const Nav = ({ state }) => {
      {/* Define menu-items constants here */}
      const items = state.source.get(`/menu/${state.theme.menuUrl}/`).items;
    
      return (
      <NavContainer>
        {items.map((item) => {
           return (
            <NavItem key={item.ID}>
               <Link link={item.url}>{item.title}</Link>
             </NavItem>
          );
        })}
      </NavContainer>
      );
    };
    
    export default connect(Nav);
    
    const NavContainer = styled.nav`
      list-style: none;
      // ...

    If we change our menu slug here and in index.js, then we get a different menu. To view dynamic menu items in mobile view, we should similarly update menu-modal.js components as well.

    Additionally, the tutorial describes how to fetch nested menus as well, which you can learn from the tutorial video, starting at about 18:09.

    Modifying the file structure

    I decided to restructure my Labre (formerly known as Mars) theme folder. Here’s how it looks after the changes:

    #! modified Frontity labre-theme structure
    packages/labre-theme/
    |__ src/
      |__ index.js
      |__ components/
         |__image/
         |__assets/
         |__ list/
         |__ footer/
           |__footer.js
           |__ widget.js
         |__ header/
           |__ header.js
           |__ menu-icon.js
           |__ menu-model.js
           |__ nav.js
         |__ pages/
           |__ index.js
           |__ page.js
         |__ posts/
           |__ index.js
           |__ post.js
         |__ styles/
         // ...

    As you can see, I added separate folders for pages, styles, headers, posts, and images. Please take a note that we have to update file paths in index.js and other related components anytime we change the way files and folders are organized. Otherwise, they’ll be pointing to nothing!

    You may have noticed that the original Mars Theme folder structure includes neither a footer component, nor a separate page component. Let’s make those components to demonstrate how our new folder structure works.

    We can start with the page component. The Mars Theme generates both pages and posts with the posts.js component by default — that’s because pages and posts are essentially the same except that posts have meta data (e.g. authors, date, etc.) and they can get away with it. But we can separate them for our own needs by copying the code in posts.js and pasting it into a new pages.js file in our /pages folder.

    // src/components/pages/page.js
    import React, { useEffect } from "react";
    import { connect, styled } from "frontity";
    import List from "../list";
    
    const Page = ({ state, actions, libraries }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
      // Get the data of the post.
      const page = state.source[data.type][data.id];
      //  ...
      // Load the page, but only if the data is ready.
      return data.isReady ? (
        <Container>
          <div className="post-title">
            <Title dangerouslySetInnerHTML={{ __html: page.title.rendered }} />
          </div>
    
          {/* Render the content using the Html2React component so the HTML is processed by the processors we included in the libraries.html2react.processors array. */}
          <Content>
            <Html2React html={page.content.rendered} />
          </Content>
        </Container>
      ) : null;
    };
    // Connect the Page component to get access to the `state` in its `props`
    export default connect(Page);
    
    // Copy styled components from post.js except, DateWrapper
    const Container = styled.div`
        width: 90vw;
        width: clamp(16rem, 93vw, 58rem);
        margin: 0;
        padding: 24px;
    `
    // ..

    All we did here was remove the meta data from post.js (lines 31-34 and 55-76) and the corresponding styled components. Just as we did with the Mars Theme /list folder, we should export the loadable function in both the pages and posts folders to code split the <List /> component. This way, the <List /> component isn’t displayed if a user is on a single post.

    // src/components/pages/index.js
    import { loadable } from "frontity";
    
    /** Codesplit the list component so it's not included
    *   if the users load a post directly. */
    export default loadable(() => import("./page"));

    Next, we should update path url of /src/components/index.js component as shown below:

    // src/components/index.js
    import { Global, css, connect, styled, Head } from "frontity";
    import Switch from "@frontity/components/switch";
    import Header from "./header/header";
    import List from "./list";
    import Page from "./pages/page";
    import Post from "./posts/post";
    import Loading from "./loading";
    import Title from "./title";
    import PageError from "./page-error";
    
    /** Theme is the root React component of our theme. The one we will export
     * in roots. */
    const Theme = ({ state }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
    
      return (
        <>
          // ...
    
          {/* Add some global styles for the whole site */}
           <Global styles={globalStyles} />
          {/* Add the header of the site. */}
          <HeadContainer>
            <Header />
          </HeadContainer>
          {/* Add the main section */}
          <Main>
            <Switch>
              <Loading when={data.isFetching} />
              <List when={data.isArchive} />
              <Page when={data.isPage} /> {/* Added Page component */}
              <Post when={data.isPostType} />
              <PageError when={data.isError} />
            </Switch>
          </Main>
        </>
      );
    };
    
    export default connect(Theme);
    
    // styled components

    Now we’re importing the <Page / component and have added our <Main /> styled component.

    Let’s move on to our custom footer component. You probably know what to do by now: create a new footer.js component file and drop it into the /src/components/footer/ folder. We can add some widgets to our footer that display the sitemap and some sort of “Powered by” blurb:

    // src/components/footer/footer.js
    import React from "react";
    import { connect, styled } from "frontity";
    import Widget from "./widget"
    
    const Footer = () => {
      return (
      <>
        <Widget />
        <footer>
          <SiteInfo>
            Frontity LABRE Theme 2021 | {" "} Proudly Powered by {"  "}
            <FooterLinks href="https://wordpress.org/" target="_blank" rel="noopener">WordPress</FooterLinks>
            {"  "} and
            <FooterLinks href="https://frontity.org/" target="_blank" rel="noopener"> Frontity</FooterLinks>
          </SiteInfo>
        </footer>
        </>
      );
    };
    
    export default connect(Footer);
    // ...

    This is a super simple example. Please note that I have imported a <Widget /> component (line 4, highlighted above) and called the component (line 9, highlighted above). We don’t actually have a <Widget /> component yet, so let’s make that while we’re at it. That can be a widget.js file in the same directory as the footer, /src/components/footer/.

    Screen shot of VS code editor open to a widget.js file that shows the syntax highlighted markup for a component.
    This widget.js component was inspired by Aamodt Group‘s footer component, available in a GitHub repository.
    Four columns of links, each with a heading. The text is dark against a light gray background.
    The widget is hard-coded but works.

    Customizing the theme header

    The default header.js component in Mars Theme is very basic with a site title and site description and navigation items underneath. I wanted to refactor the header component with a site logo and title on the left and the nav.js component (top navigation) on the right.

    // src/components/header.js
    import { connect, styled } from "frontity";
    import Link from "./link";
    import Nav from "./nav";
    import MobileMenu from "./menu";
    import logo from "./images/frontity.png"
    
    const Header = ({ state }) => {
      return (
        <>
          <Container>
            <StyledLink link="/">
             {/* Add header logo*/}
              <Logo src={logo} />
              <Title>{state.frontity.title}</Title>
            </StyledLink>
              {/*<Description>{state.frontity.description}</Description> */}
              <Nav />
          </Container>
            <MobileMenu />
        </>
      );
    };
    // Connect the Header component to get access to the `state` in its `props`
    export default connect(Header);
    
    const Container = styled.div`
      width: 1000px;
      // ...
      `}
    {/* Logo styled component */}
    const Logo = styled.img`
      max-width: 30px;
      display: inline-block;
      border-radius: 15px;
      margin-right: 15px;
    `;
    
    // ...

    My refactored header.js component imports a logo image (line 6, highlighted above) and uses in line 14. The nav.js component shown below is basically the same, only with some minor styling modifications.

    Screenshot showing refactored site header with site logo and site title (left) and top navigation (right)

    Adding the <Global> style component

    We have already covered the <Global> component and how it’s used for site-wide CSS. There are only a few global styles in the default Mars Theme root component, and I wanted to add more.

    I did that with a separate globalStyles file at /src/components/styles/globalStyles.js — similar to Frontity’s Twenty Twenty theme — and added root variables, a CSS reset, and common site-wide element styles, found in the GitHub repo.

    Implementing fluid typography

    Even though it’s not really in scope, I really wanted to use fluid typography in my custom theme as part of my overall learning journey. So, I added it to the global styles.

    CSS-Tricks has extensively covered fluid typography and how the clamp() function is used to set target font sizes. Following those CSS-Tricks posts and this Picalilli one as my guide, I defined two custom properties with clamped font size ranges on the :root element in the globalStyles.js component.

    // src/components/styles/globalStyles.js
    :root {
      --wide-container: clamp(16rem, 90vw, 70rem);
      --normal-container: clamp(16rem, 90vw, 58rem);
    }

    The wide-container wrapper is used for header and footer components whereas the normal-container will be used for displaying posts and pages.

    I also clamped the headings under elementBase in the globalStyles.js component as shown in this GitHub repo.

    It was a fun working with the clamp() function because it meant I could set a range of sizes without any media queries at all!

    Adding webfonts to the theme

    I also wanted to use a different webfont in my theme. Importing webfonts in CSS using @font-face is covered here on CSS-Tricks. Frontity’s Twenty Twenty Theme uses it, so that’s a good place to reference as well.

    I wanted three Google fonts:

    We can use the fonts with either with a <link>in the HTML head or with @import in CSS. But Chris covered how to use @font-face with Google Fonts, which allows us to optimize the number of HTTP requests we make since we can download the fonts to our own server.

    I use the Google webfonts helper to host the downloaded font files. Here’s what I got:

    /* source: google webfonts helper */
    /* source-sans-pro-regular - latin */
    @font-face {
      font-family: 'Source Sans Pro';
      font-style: normal;
      font-weight: 400;
      src: url('../fonts/source-sans-pro-v14-latin-regular.eot'); /* IE9 Compat Modes */
      src: local(''),
        url('../fonts/source-sans-pro-v14-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
        url('../fonts/source-sans-pro-v14-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
        url('../fonts/source-sans-pro-v14-latin-regular.woff') format('woff'), /* Modern Browsers */
        url('../fonts/source-sans-pro-v14-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
        url('../fonts/source-sans-pro-v14-latin-regular.svg#SourceSansPro') format('svg'); /* Legacy iOS */
    }

    Looking at the Twenty Twenty Theme as a reference for how it’s done there, I created a font-face.js file and dropped it into the /src/components/styles folder as shown in this GitHub repository.

    Those fonts point to a /fonts folder that doesn’t exist. So, let’s make one there and make sure all of the correct font files are in it so the fonts load properly.

    Importing globalStyles and @face-font components to the root <Theme /> component

    Let’s open our theme root component, /src/components.index.js, and add our globalStyles.js and font-face.js components in there. As shown below, we should import both components into index.js and call the components later.

    // src/components/index.js
    
    // ...
    import FontFace from "./styles/font-face";
    import globalStyles from "./styles/globalStyles";
    
    /** Theme is the root React component of our theme. The one we will export
     * in roots. */
    const Theme = ({ state }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
    
      return (
        <>
        // ...
    
        {/* Add some global styles for the whole site, like body or a's.
         *  Not classes here because we use CSS-in-JS. Only global HTML tags. */}
          <Global styles={globalStyles} />
          <FontFace />
          {/* Add the header of the site. */}
          // ...
    
    export default connect(Theme);
    
     {/* delete original globalStyles css component */}
    
     // ...

    Finally, we should remove mars-theme globalStyles component from index.js. Now our new fonts are applied throughout our project.

    Styling pages and posts

    Our posts and pages are pretty much styled already, except for some Gutenberg block contents, like buttons, quotes, etc.

    To style our post entry meta data, let’s add icons for the author, date, categories, and tags. Frontity’s port of the WordPress Twenty Nineteen theme uses SVG icons and components for author.js, categories.js, posted-on.js and tags.js components, which we can totally copy and use in our own project. I literally copied the top-level entry-meta folder and everything in it from the frontity-twentynineteen theme and added it all to the /components/posts/ project folder.

    Next we should update our src/components/list/list-item.js component so we can use the new assets:

    // src/components/list/list-item.js
    
    import { connect, styled } from "frontity";
    import Link from "../link";
    import FeaturedMedia from "../featured-media";
    
    // import entry-meta
    import Author from "../entry-meta/author";
    import PostedOn from "../entry-meta/posted-on";
    
    const Item = ({ state, item }) => {
    
      return (
        <article>
          <div>
            {/* If the post has an author, we render a clickable author text. */}
            <EntryMeta>
              <Author authorId={item.author} /> {"|  "}
              <PostedOn post={item} />
            </EntryMeta>
          </div>
    
          <Link link={item.link}>
            <Title dangerouslySetInnerHTML={{ __html: item.title.rendered }} />
          </Link>
          // ...
        </article>
      );
    };
    
    // Connect the Item to gain access to `state` as a prop
    export default connect(Item);

    The styled component for the <EntryMeta /> component can be something like as shown in the GitHub repository.

    With these styles in place, our archive page entry meta looks good with icons displayed before entry-meta taxonomy (authors, posted-on).

    Here we will modify archives taxonomy page styling with more descriptive header. Let’s update list.js component of our /src/components/list/list.js as shown below.

    // src/components/list/list.js
    
    import React from "react";
    import { connect, styled, decode } from "frontity";
    import Item from "./list-item";
    import Pagination from "./pagination";
    
    const List = ({ state }) => {
      // Get the data of the current list.
      const data = state.source.get(state.router.link);
    
      return (
        <Container className="entry-content">
          {/* If the list is a taxonomy, we render a title. */}
          {data.isAuthor ? (
            <Header>
              Author Archives:{" "}
              <PageDescription>
              {decode(state.source.author[data.id].name)}
              </PageDescription>
            </Header>
            ) : null}
    
            {/* If the list is a taxonomy or category, we render a title. */}
            {data.isTaxonomy || data.isCategory ? (
              <Header>
                {data.taxonomy.charAt(0).toUpperCase() + data.taxonomy.slice(1)}{" "}
                Archives:{" "}
                <PageDescription>
                {decode(state.source[data.taxonomy][data.id].name)}
                </PageDescription>
              </Header>
            ) : null}
          // ...
    
          <Pagination />
        </Container>
      );
    };
    export default connect(List);
    
    const PageDescription = styled.span`
      font-weight: bold;
      font-family: var(--body-family);
        color: var(--color-text);
    `;
    // ...

    In the example above, we wrapped taxonomy.id data with PageDesctiption styled component applied some styling rules.

    The post pagination in the default Mars Theme is very basic with almost no styling. Let’s borrow from the Frontity Twenty Nineteen theme again and add the pagination component and styling from the theme by copying the pagination.js component file in its entirety, and paste it to /src/components/list/pagination.js in our theme.

    Showing two example posts in a post list, one with comments enabled, and the other with comments disabled. The post content is black against a light gray background.
    I added some minor CSS and it works perfectly in our project.

    To customize the actual individual posts and pages, let’s make bold header title that’s centered and displays the entry meta:

    // src/components/posts/post.js
    
    // ...
    // Import entry-meta
    import Author from "../entry-meta/author";
    import PostedOn from "../entry-meta/posted-on";
    import Categories from "../entry-meta/categories";
    import Tags from "../entry-meta/tags";
    
    const Post = ({ state, actions, libraries }) => {
      // ...
      // Load the post, but only if the data is ready.
      return data.isReady ? (
        <Container className="main">
          <div>
            <Title dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
    
            {/* Hide author and date on pages */}
            {data.isPost && (
              <EntryMeta>
              <Author authorId={post.author} />
              <PostedOn post={post} />
            </EntryMeta>
            )}
          </div>
    
          {/* Look at the settings to see if we should include the featured image */}
          {state.theme.featured.showOnPost && (
            <FeaturedMedia id={post.featured_media} />
          )}
    
          {data.isAttachment ? (
            <div dangerouslySetInnerHTML={{ __html: post.description.rendered }} />
          ) : (
            <Content>
              <Html2React html={post.content.rendered} />
              {/* Add footer meta-entry */}
              <EntryFooter>
                <Categories cats={post.categories} />
                <Tags tags={post.tags} />
              </EntryFooter>
            </Content>
          )}
        </Container>
      ) : null;
    };
    
    export default connect(Post);
    // ...
    Screenshot showing header meta-entry (top) and footer meta-entry (bottom). Header has a large bold title above meta containing the author name and post date, all centered. There is a thick line between the header and content. The content is a simple paragraph containing lorem ipsum text. Dark content against a light gray background.

    Adding Gutenberg block styles

    WordPress uses a separate stylesheet for blocks in the Block Editor. Right now, that stylesheet isn’t being used but it would be great if we could get some base styles in there that we use for the various block content we add to pages and posts.

    A post with DevTools open and highlighting the markup for the button component.
    That .wp-block-buttons class is declared in the WordPress blocks stylesheet that we aren’t using… yet.

    The WordPress Block Editor uses two styling files: style.css and theme.css. Let’s copy these directly from Frontity’s port of the Twenty Twenty theme because that’s how they implemented the WordPress styles. We can place those inside a /styles/gutenberg/ folder.

    “Gutenberg” is the codename that was given to the WordPress Block Editor when it was in development. It’s sometimes still referred to that way.

    Let’s add the above two style files to our theme root component, /src/components/index.js, just like we did earlier for globalStyles:

    //  src/components/index.js
    import gutenbergStyle from "./styles/gutenberg/style.css";
    import gutenbergTheme from "./styles/gutenberg/theme.css"

    Here’s our updated <Theme /> root component:

    // src/components/index.js
    
    // ...
    import FontFace from "./styles/font-face";
    import globalStyles from "./styles/globalStyles";
    // Add Gutenberg styles
    import gutenbergStyle from "./styles/gutenberg/style.css";
    import gutenbergTheme from "./styles/gutenberg/theme.css"
    
    /** Theme is the root React component of our theme. The one we will export
      * in roots. */
    const Theme = ({ state }) => {
      // Get information about the current URL.
      const data = state.source.get(state.router.link);
    
      return (
        <>
        // ...
        {/* Add some global styles for the whole site, like body or a's.
          * Not classes here because we use CSS-in-JS. Only global HTML tags. */}
          <Global styles={globalStyles} />
          <Global styles={css(gutenbergStyle)} />
          <Global styles={css(gutenbergTheme)} />
          <FontFace />
          {/* Add the header of the site. */}
          // ...
    export default connect(Theme);
    
     {/* Delete original globalStyles css component */}
     // ...

    We could go about overriding styles many different ways. I went with a simple route. For example, to overriding button styles — .wp-block-buttons — in the styled-component for pages and posts.

    Screenshot showing button style customization (left panel) and styled button in blue (right)

    We can write override any other block styles the same way. In Frontity’s Twenty Nineteen theme, the entire stylesheet from the WordPress version of the theme is added to the Frontity version to replicate the exact same appearance. Frontity’s Twenty Twenty port uses only a select few of the styles in the WordPress Twenty Twenty themes, but as inline styles.

    Additional styling resources

    All the resources we covered in this section on styling are available in the GitHub repository. If you wish to expand my @frontity/labre-theme project further, here are the resources that I gathered.

    Section 6: Resources and credit

    There are ample resources to learn and customize your Frontity project. While preparing this post, I have referred to the following resources extensively. Please refer to original posts for more detailed information.

    Frontity documentation and articles

    • Step-by-step tutorial (Frontity): This is the perfect place to start if you’re new to Frontity, or even if you’ve previously used Frontity and want to level up.
    • Conceptial guides (Frontity): These guides helps solve some of the common challenges that come up when working with dynamic server-side rendering in React apps connected to WordPress.
    • Frontity API reference (Frontity). This contains detailed information about Frontity CLI, packages, plugins and themes. Once you’ve mastered the basics of working with Frontity, this is where you’re likely to spend most of your time when working on projects.”
    • Frontity example repo (Frontity): This is a collection of Frontity projects that demonstrate how Frontity is used in the wild.

    Frontity case studies

    Frontity talks and videos

    Frontity community

    Frontity has a vibrant and engaging community forum for asking questions or getting help regarding your Frontity project.

    Wrapping up and personal thoughts

    If you can’t already tell from this post or the others I’ve written, I have a huge passion for headless WordPress sites. As I wrote in a previous article, I came across Frontity through when Chris posted this article. I have been experimenting with it for over six months, choosing to take a deep drive into Frontity and the building blocks used in its default Mars Theme. I must admit that it’s a fascinating software framework and I’ve had an enjoyable learning experience. I may even use this sort of setup for my own personal site!

    Here are a few key takeaways from my experience working with Frontity so far:

    • It’s beginner-friendly and low maintenance: One of the things that impressed me most with Frontity is how relatively easy it is to jump into, even as a beginner. It installs with a couple of commands and takes care of all the setup and configuration for connecting to WordPress via the REST API—something I would have struggled with if left to my own devices.
    • It works with experimental block themes. In my very limited testing, Frontity’s framework works as expected with experimental block themes, just as it does with classic WordPress themes, like Twenty Twenty. I tested with the Quadrat theme that supports the experimental stuff the Gutenberg team is working on.
    • Hosting is good, but maybe too expensive: As Chris wrote, Frontity is “a perfect match for Vercel.” However, the current Jamstack pricing model that includes Vercel is unattractive for many ordinary WordPress users.
    • Frontity’s documentation is good, but could be better: The Frontity team recently reorganized Frontity documentation into tutorials, guides and an API reference. However, in my opinion it’s still confusing for those just getting into the framework.

    Because I enjoyed this project so much, I am currently doing a theme project from scratch. Even in WordPress, I learned best by getting my hands dirty building WordPress themes from scratch.

    While I am still doing my Gatsby and Frontity side projects, I have not lost my sight from the ongoing WordPress block editor and block-based theme development. At the time of writing, there are already sixteen block-based themes in the WordPress theme directory. I have just started exploring and understanding experimental block themes, which might be another interesting learning project.

    After this project, my thoughts about Gatsby, Frontity and the concept of headless sites are still evolving. That’s only because it’s tough to make a fair comparison of when a lot of the tooling is actively in development and changing all the time. There are even experimental themes, that are much lighter and different structural markups than the current PHP-based classic themes, which might be a subject for yet another time.


    Please share your experience and thoughts if you have been using Frontity in your projects. As always, I enjoy reading any comments and feedback!


    Mars Theme: A Deep Look at Frontity’s Headless WordPress Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/mars-theme-a-deep-look-at-frontitys-headless-wordpress-theme/feed/ 3 350070
    Accessing Your Data With Netlify Functions and React https://css-tricks.com/accessing-data-netlify-functions-react/ https://css-tricks.com/accessing-data-netlify-functions-react/#comments Thu, 02 Sep 2021 00:28:22 +0000 https://css-tricks.com/?p=351172 (This is a sponsored post.)

    Static site generators are popular for their speed, security, and user experience. However, sometimes your application needs data that is not available when the site is built. React is a library for building user …


    Accessing Your Data With Netlify Functions and React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    (This is a sponsored post.)

    Static site generators are popular for their speed, security, and user experience. However, sometimes your application needs data that is not available when the site is built. React is a library for building user interfaces that helps you retrieve and store dynamic data in your client application. 

    Fauna is a flexible, serverless database delivered as an API that completely eliminates operational overhead such as capacity planning, data replication, and scheduled maintenance. Fauna allows you to model your data as documents, making it a natural fit for web applications written with React. Although you can access Fauna directly via a JavaScript driver, this requires a custom implementation for each client that connects to your database. By placing your Fauna database behind an API, you can enable any authorized client to connect, regardless of the programming language.

    Netlify Functions allow you to build scalable, dynamic applications by deploying server-side code that works as API endpoints. In this tutorial, you build a serverless application using React, Netlify Functions, and Fauna. You learn the basics of storing and retrieving your data with Fauna. You create and deploy Netlify Functions to access your data in Fauna securely. Finally, you deploy your React application to Netlify.

    Getting started with Fauna

    Fauna is a distributed, strongly consistent OLTP NoSQL serverless database that is ACID-compliant and offers a multi-model interface. Fauna also supports document, relational, graph, and temporal data sets from a single query. First, we will start by creating a database in the Fauna console by selecting the Database tab and clicking on the Create Database button.

    Next, you will need to create a Collection. For this, you will need to select a database, and under the Collections tab, click on Create Collection.

    Fauna uses a particular structure when it comes to persisting data. The design consists of attributes like the example below.

    {
      "ref": Ref(Collection("avengers"), "299221087899615749"),
      "ts": 1623215668240000,
      "data": {
        "id": "db7bd11d-29c5-4877-b30d-dfc4dfb2b90e",
        "name": "Captain America",
        "power": "High Strength",
        "description": "Shield"
      }
    }

    Notice that Fauna keeps a ref column which is a unique identifier used to identify a particular document. The ts attribute is a timestamp to determine the time of creating the record and the data attribute responsible for the data.

    Why creating an index is important

    Next, let’s create two indexes for our avengers collection. This will be pretty valuable in the latter part of the project. You can create an index from the Index tab or from the Shell tab, which provides a console to execute scripts. Fauna supports two types of querying techniques: FQL (Fauna’s Query language) and GraphQL. FQL operates based on the schema of Fauna, which includes documents, collections, indexes, sets, and databases. 

    Let’s create the indexes from the shell.

    This command will create an index on the Collection, which will create an index by the id field inside the data object. This index will return a ref of the data object. Next, let’s create another index for the name attribute and name it avenger_by_name.

    Creating a server key

    To create a server key, we need to navigate the Security tab and click on the New Key button. This section will prompt you to create a key for a selected database and the user’s role.

    Getting started with Netlify functions and React

    In this section, we’ll see how we create Netlify functions with React. We will be using create-react-app to create the react app.

    npx create-react-app avengers-faunadb

    After creating the react app, let’s install some dependencies, including Fauna and Netlify dependencies.

    yarn add axios bootstrap node-sass uuid faunadb react-netlify-identity react-netlify-identity-widget

    Now let’s create our first Netlfiy function. To make the functions, first, we need to install Netlifiy CLI globally.

    npm install netlify-cli -g

    Now that the CLI is installed, let’s create a .env file on our project root with the following fields.

    FAUNADB_SERVER_SECRET= <FaunaDB secret key>
    REACT_APP_NETLIFY= <Netlify app url>

    Next, Let’s see how we can start with creating netlify functions. For this, we will need to create a directory in our project root called functions and a file called netlify.toml, which will be responsible for maintaining configurations for our Netlify project. This file defines our function’s directory, build directory, and commands to execute.

    [build]
    command = "npm run build"
    functions = "functions/"
    publish = "build"
    
    [[redirects]]
      from = "/api/*"
      to = "/.netlify/functions/:splat"
      status = 200
      force = true

    We will do some additional configuration for the Netlify configuration file, like in the redirection section in this example. Notice that we are changing the default path of the Netlify function of /.netlify/** to /api/. This configuration is mainly for the improvement of the look and field of the API URL. So to trigger or call our function, we can use the path:

    https://domain.com/api/getPokemons

     …instead of:

    https://domain.com/.netlify/getPokemons

    Next, let’s create our Netlify function in the functions directory. But, first, let’s make a connection file for Fauna called util/connections.js, returning a Fauna connection object.

    const faunadb = require('faunadb');
    const q = faunadb.query
    
    const clientQuery = new faunadb.Client({
      secret: process.env.FAUNADB_SERVER_SECRET,
    });
    
    module.exports = { clientQuery, q };

    Next, let’s create a helper function checking for reference and returning since we will need to parse the data on several occasions throughout the application. This file will be util/helper.js.

    const responseObj = (statusCode, data) => {
      return {
        statusCode: statusCode,
        headers: {
         /* Required for CORS support to work */
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Headers": "*",
          "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
        },
       body: JSON.stringify(data)
      };
    };
    
    const requestObj = (data) => {
      return JSON.parse(data);
    }
    
    module.exports = { responseObj: responseObj, requestObj: requestObj }

    Notice that the above helper functions handle the CORS issues, stringifying and parsing of JSON data. Let’s create our first function, getAvengers, which will return all the data.

    const { responseObj } = require('./util/helper');
    const { q, clientQuery } = require('./util/connection');
    
    exports.handler = async (event, context) => {
      try {
       let avengers = await clientQuery.query(
         q.Map(
           q.Paginate(q.Documents(q.Collection('avengers'))),
           q.Lambda(x => q.Get(x))
          )
        )
        return responseObj(200, avengers)
      } catch (error) {
        console.log(error)
        return responseObj(500, error);
      }
    };

    In the above code example, you can see that we have used several FQL commands like Map, Paginate, Lamda. The Map key is used to iterate through the array, and it takes two arguments: an Array and Lambda. We have passed the Paginate for the first parameter, which will check for reference and return a page of results (an array). Next, we used a Lamda statement, an anonymous function that is quite similar to an anonymous arrow function in ES6.

    Next, Let’s create our function AddAvenger responsible for creating/inserting data into the Collection.

    const { requestObj, responseObj } = require('./util/helper');
    const { q, clientQuery } = require('./util/connection');
    
    exports.handler = async (event, context) => {
      let data = requestObj(event.body);
    
      try {
        let avenger = await clientQuery.query(
          q.Create(
            q.Collection('avengers'),
            {
              data: {
                id: data.id,
                name: data.name,
                power: data.power,
                description: data.description
              }
            }
          )
        );
    
        return responseObj(200, avenger)
      } catch (error) {
        console.log(error)
        return responseObj(500, error);
      }
     
    };

    To save data for a particular collection, we will have to pass, or data to the data:{} object like in the above code example. Then we need to pass it to the Create function and point it to the collection you want and the data. So, let’s run our code and see how it works through the netlify dev command.

    Let’s trigger the GetAvengers function through the browser through the URL http://localhost:8888/api/GetAvengers.

    The above function will fetch the avenger object by the name property searching from the avenger_by_name index. But, first, let’s invoke the GetAvengerByName function through a Netlify function. For that, let’s create a function called SearchAvenger.

    const { responseObj } = require('./util/helper');
    const { q, clientQuery } = require('./util/connection');
    
    exports.handler = async (event, context) => {
      const {
        queryStringParameters: { name },
      } = event;
    
      try {
        let avenger = await clientQuery.query(
          q.Call(q.Function("GetAvengerByName"), [name])
        );
        return responseObj(200, avenger)
      } catch (error) {
        console.log(error)
        return responseObj(500, error);
      }
    };

    Notice that the Call function takes two arguments where the first parameter will be the reference for the FQL function that we created and the data that we need to pass to the function.

    Calling the Netlify function through React

    Now that several functions are available let’s consume those functions through React. Since the functions are REST APIs, let’s consume them via Axios, and for state management, let’s use React’s Context API. Let’s start with the Application context called AppContext.js.

    import { createContext, useReducer } from "react";
    import AppReducer from "./AppReducer"
    
    const initialState = {
        isEditing: false,
        avenger: { name: '', description: '', power: '' },
        avengers: [],
        user: null,
        isLoggedIn: false
    };
    
    export const AppContext = createContext(initialState);
    
    export const AppContextProvider = ({ children }) => {
        const [state, dispatch] = useReducer(AppReducer, initialState);
    
        const login = (data) => { dispatch({ type: 'LOGIN', payload: data }) }
        const logout = (data) => { dispatch({ type: 'LOGOUT', payload: data }) }
        const getAvenger = (data) => { dispatch({ type: 'GET_AVENGER', payload: data }) }
        const updateAvenger = (data) => { dispatch({ type: 'UPDATE_AVENGER', payload: data }) }
        const clearAvenger = (data) => { dispatch({ type: 'CLEAR_AVENGER', payload: data }) }
        const selectAvenger = (data) => { dispatch({ type: 'SELECT_AVENGER', payload: data }) }
        const getAvengers = (data) => { dispatch({ type: 'GET_AVENGERS', payload: data }) }
        const createAvenger = (data) => { dispatch({ type: 'CREATE_AVENGER', payload: data }) }
        const deleteAvengers = (data) => { dispatch({ type: 'DELETE_AVENGER', payload: data }) }
    
        return <AppContext.Provider value={{
            ...state,
            login,
            logout,
            selectAvenger,
            updateAvenger,
            clearAvenger,
            getAvenger,
            getAvengers,
            createAvenger,
            deleteAvengers
        }}>{children}</AppContext.Provider>
    }
    
    export default AppContextProvider;

    Let’s create the Reducers for this context in the AppReducer.js file, Which will consist of a reducer function for each operation in the application context.

    const updateItem = (avengers, data) => {
        let avenger = avengers.find((avenger) => avenger.id === data.id);
        let updatedAvenger = { ...avenger, ...data };
        let avengerIndex = avengers.findIndex((avenger) => avenger.id === data.id);
        return [
            ...avengers.slice(0, avengerIndex),
            updatedAvenger,
            ...avengers.slice(++avengerIndex),
        ];
    }
    
    const deleteItem = (avengers, id) => {
        return avengers.filter((avenger) => avenger.data.id !== id)
    }
    
    const AppReducer = (state, action) => {
        switch (action.type) {
            case 'SELECT_AVENGER':
                return {
                    ...state,
                    isEditing: true,
                    avenger: action.payload
                }
            case 'CLEAR_AVENGER':
                return {
                    ...state,
                    isEditing: false,
                    avenger: { name: '', description: '', power: '' }
                }
            case 'UPDATE_AVENGER':
                return {
                    ...state,
                    isEditing: false,
                    avengers: updateItem(state.avengers, action.payload)
                }
            case 'GET_AVENGER':
                return {
                    ...state,
                    avenger: action.payload.data
                }
            case 'GET_AVENGERS':
                return {
                    ...state,
                    avengers: Array.isArray(action.payload && action.payload.data) ? action.payload.data : [{ ...action.payload }]
                };
            case 'CREATE_AVENGER':
                return {
                    ...state,
                    avengers: [{ data: action.payload }, ...state.avengers]
                };
            case 'DELETE_AVENGER':
                return {
                    ...state,
                    avengers: deleteItem(state.avengers, action.payload)
                };
            case 'LOGIN':
                return {
                    ...state,
                    user: action.payload,
                    isLoggedIn: true
                };
            case 'LOGOUT':
                return {
                    ...state,
                    user: null,
                    isLoggedIn: false
                };
            default:
                return state
        }
    }
    
    export default AppReducer;
    

    Since the application context is now available, we can fetch data from the Netlify functions that we have created and persist them in our application context. So let’s see how to call one of these functions.

    const { avengers, getAvengers } = useContext(AppContext);
    
    const GetAvengers = async () => {
      let { data } = await axios.get('/api/GetAvengers);
      getAvengers(data)
    }

    To get the data to the application contexts, let’s import the function getAvengers from our application context and pass the data fetched by the get call. This function will call the reducer function, which will keep the data in the context. To access the context, we can use the attribute called avengers. Next, let’s see how we could save data on the avengers collection.

    const { createAvenger } = useContext(AppContext);
    
    const CreateAvenger = async (e) => {
      e.preventDefault();
      let new_avenger = { id: uuid(), ...newAvenger }
      await axios.post('/api/AddAvenger', new_avenger);
      clear();
      createAvenger(new_avenger)
    }

    The above newAvenger object is the state object which will keep the form data. Notice that we pass a new id of type uuid to each of our documents. Thus, when the data is saved in Fauna, We will be using the createAvenger function in the application context to save the data in our context. Similarly, we can invoke all the netlify functions with CRUD operations like this via Axios.

    How to deploy the application to Netlify

    Now that we have a working application, we can deploy this app to Netlify. There are several ways that we can deploy this application:

    1. Connecting and deploying the application through GitHub
    2. Deploying the application through the Netlify CLI

    Using the CLI will prompt you to enter specific details and selections, and the CLI will handle the rest. But in this example, we will be deploying the application through Github. So first, let’s log in to the Netlify dashboard and click on New Site from Git button. Next, It will prompt you to select the Repo you need to deploy and the configurations for your site like build command, build folder, etc.

    How to authenticate and authorize functions by Netlify Identity

    Netlify Identity provides a full suite of authentication functionality to your application which will help us to manage authenticated users throughout the application. Netlify Identity can be integrated easily into the application without using any other 3rd party service and libraries. To enable Netlify Identity, we need to login into our Neltify dashboard, and under our deployed site, we need to move to the Identity tab and allow the identity feature.

    Enabling Identity will provide a link to your netlify identity. You will have to copy that URL and add it to the .env file of your application for REACT_APP_NETLIFY. Next, We need to add the Netlify Identity to our React application through the netlify-identity-widget and the Netlify functions. But, first, let’s add the REACT_APP_NETLIFY property for the Identity Context Provider component in the index.js file.

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import "react-netlify-identity-widget/styles.css"
    import 'bootstrap/dist/css/bootstrap.css';
    import App from './App';
    import { IdentityContextProvider } from "react-netlify-identity-widget"
    const url = process.env.REACT_APP_NETLIFY;
    
    ReactDOM.render(
      <IdentityContextProvider url={url}>
        <App />
      </IdentityContextProvider>,
      document.getElementById('root')
    );

    This component is the Navigation bar that we use in this application. This component will be on top of all the other components to be the ideal place to handle the authentication. This react-netlify-identity-widget will add another component that will handle the user signI= in and sign up.

    Next, let’s use the Identity in our Netlify functions. Identity will introduce some minor modifications to our functions, like the below function GetAvenger.

    const { responseObj } = require('./util/helper');
    const { q, clientQuery } = require('./util/connection');
    
    exports.handler = async (event, context) => {
        if (context.clientContext.user) {
            const {
                queryStringParameters: { id },
            } = event;
            try {
                const avenger = await clientQuery.query(
                    q.Get(
                        q.Match(q.Index('avenger_by_id'), id)
                    )
                );
                return responseObj(200, avenger)
            } catch (error) {
                console.log(error)
                return responseObj(500, error);
            }
        } else {
            return responseObj(401, 'Unauthorized');
        }
    };

    The context of each request will consist of a property called clientContext, which will consist of authenticated user details. In the above example, we use a simple if condition to check the user context. 

    To get the clientContext in each of our requests, we need to pass the user token through the Authorization Headers. 

    const { user } = useIdentityContext();
    
    const GetAvenger = async (id) => {
      let { data } = await axios.get('/api/GetAvenger/?id=' + id, user && {
        headers: {
          Authorization: `Bearer ${user.token.access_token}`
        }
      });
      getAvenger(data)
    }

    This user token will be available in the user context once logged in to the application through the netlify identity widget.

    As you can see, Netlify functions and Fauna look to be a promising duo for building serverless applications. You can follow this GitHub repo for the complete code and refer to this URL for the working demo.

    Conclusion

    In conclusion, Fauna and Netlify look to be a promising duo for building serverless applications. Netlify also provides the flexibility to extend its functionality through the plugins to enhance the experience. The pricing plan with pay as you go is ideal for developers to get started with fauna. Fauna is extremely fast, and it auto-scales so that developers will have the time to focus on their development more than ever. Fauna can handle complex database operations where you would find in Relational, Document, Graph, Temporal databases. Fauna Driver support all the major languages such as Android, C#, Go, Java, JavaScript, Python, Ruby, Scala, and Swift. With all these excellent features, Fauna looks to be one of the best Serverless databases. For more information, go through Fauna documentation.


    Accessing Your Data With Netlify Functions and React originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/accessing-data-netlify-functions-react/feed/ 1 351172
    Three Buggy React Code Examples and How to Fix Them https://css-tricks.com/three-buggy-react-code-examples-and-how-to-fix-them/ https://css-tricks.com/three-buggy-react-code-examples-and-how-to-fix-them/#respond Thu, 05 Aug 2021 14:24:36 +0000 https://css-tricks.com/?p=345418 There’s usually more than one way to code a thing in React. And while it’s possible to create the same thing different ways, there may be one or two approaches that technically work “better” than others. I actually run into …


    Three Buggy React Code Examples and How to Fix Them originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    There’s usually more than one way to code a thing in React. And while it’s possible to create the same thing different ways, there may be one or two approaches that technically work “better” than others. I actually run into plenty of examples where the code used to build a React component is technically “correct” but opens up issues that are totally avoidable.

    So, let’s look at some of those examples. I’m going to provide three instances of “buggy” React code that technically gets the job done for a particular situation, and ways it can be improved to be more maintainable, resilient, and ultimately functional.

    This article assumes some knowledge of React hooks. It isn’t an introduction to hooks—you can find a good introduction from Kingsley Silas on CSS Tricks, or take a look at the React docs to get acquainted with them. We also won’t be looking at any of that exciting new stuff coming up in React 18. Instead, we’re going to look at some subtle problems that won’t completely break your application, but might creep into your codebase and can cause strange or unexpected behavior if you’re not careful.

    Buggy code #1: Mutating state and props

    It’s a big anti-pattern to mutate state or props in React. Don’t do this!

    This is not a revolutionary piece of advice—it’s usually one of the first things you learn if you’re getting started with React. But you might think you can get away with it (because it seems like you can in some cases).

    I’m going to show you how bugs might creep into your code if you’re mutating props. Sometimes you’ll want a component that will show a transformed version of some data. Let’s create a parent component that holds a count in state and a button that will increment it. We’ll also make a child component that receives the count via props and shows what the count would look like with 5 added to it.

    Here’s a Pen that demonstrates a naïve approach:

    This example works. It does what we want it to do: we click the increment button and it adds one to the count. Then the child component is re-rendered to show what the count would look like with 5 added on. We changed the props in the child here and it works fine! Why has everybody been telling us mutating props is so bad?

    Well, what if later we refactor the code and need to hold the count in an object? This might happen if we need to store more properties in the same useState hook as our codebase grows larger.

    Instead of incrementing the number held in state, we increment the count property of an object held in state. In our child component, we receive the object through props and add to the count property to show what the count would look like if we added 5.

    Let’s see how this goes. Try incrementing the state a few times in this pen:

    Oh no! Now when we increment the count it seems to add 6 on every click! Why is this happening? The only thing that changed between these two examples is that we used an object instead of a number!

    More experienced JavaScript programmers will know that the big difference here is that primitive types such as numbers, booleans and strings are immutable and passed by value, whereas objects are passed by reference.

    This means that:

    • If you put a number in a variable, assign another variable to it, then change the second variable, the first variable will not be changed.
    • If you if you put an object in a variable, assign another variable to it, then change the second variable, the first variable will get changed.

    When the child component changes a property of the state object, it’s adding 5 to the same object React uses when updating the state. This means that when our increment function fires after a click, React uses the same object after it has been manipulated by our child component, which shows as adding 6 on every click.

    The solution

    There are multiple ways to avoid these problems. For a situation as simple as this, you could avoid any mutation and express the change in a render function:

    function Child({state}){
      return <div><p>count + 5 = {state.count + 5} </p></div>
    }

    However, in a more complicated case, you might need to reuse state.count + 5 multiple times or pass the transformed data to multiple children.

    One way to do this is to create a copy of the prop in the child, then transform the properties on the cloned data. There’s a couple of different ways to clone objects in JavaScript with various tradeoffs. You can use object literal and spread syntax:

    function Child({state}){
    const copy = {...state};
      return <div><p>count + 5 = {copy.count + 5} </p></div>
    }

    But if there are nested objects, they will still reference the old version. Instead, you could convert the object to JSON then immediately parse it:

    JSON.parse(JSON.stringify(myobject))
    

    This will work for most simple object types. But if your data uses more exotic types, you might want to use a library. A popular method would be to use lodash’s deepClone. Here’s a Pen that shows a fixed version using object literal and spread syntax to clone the object:

    One more option is to use a library like Immutable.js. If you have a rule to only use immutable data structures, you’ll be able to trust that your data won’t get unexpectedly mutated. Here’s one more example using the immutable Map class to represent the state of the counter app:

    Buggy code #2: Derived state

    Let’s say we have a parent and a child component. They both have useState hooks holding a count. And let’s say the parent passes its state down as prop down to the child, which the child uses to initialize its count.

    function Parent(){
      const [parentCount,setParentCount] = useState(0);
      return <div>
        <p>Parent count: {parentCount}</p>
        <button onClick={()=>setParentCount(c=>c+1)}>Increment Parent</button>
        <Child parentCount={parentCount}/>
      </div>;
    }
    
    function Child({parentCount}){
     const [childCount,setChildCount] = useState(parentCount);
      return <div>
        <p>Child count: {childCount}</p>
        <button onClick={()=>setChildCount(c=>c+1)}>Increment Child</button>
      </div>;
    }

    What happens to the child’s state when the parent’s state changes, and the child is re-rendered with different props? Will the child state remain the same or will it change to reflect the new count that was passed to it?

    We’re dealing with a function, so the child state should get blown away and replaced right? Wrong! The child’s state trumps the new prop from the parent. After the child component’s state is initialized in the first render, it’s completely independent from any props it receives.

    React stores component state for each component in the tree and the state only gets blown away when the component is removed. Otherwise, the state won’t be affected by new props.

    Using props to initialize state is called “derived state” and it is a bit of an anti-pattern. It removes the benefit of a component having a single source of truth for its data.

    Using the key prop

    But what if we have a collection of items we want to edit using the same type of child component, and we want the child to hold a draft of the item we’re editing? We’d need to reset the state of the child component each time we switch items from the collection.

    Here’s an example: Let’s write an app where we can write a daily list of five thing’s we’re thankful for each day. We’ll use a parent with state initialized as an empty array which we’re going to fill up with five string statements.

    Then we’ll have a a child component with a text input to enter our statement.

    We’re about to use a criminal level of over-engineering in our tiny app, but it’s to illustrate a pattern you might need in a more complicated project: We’re going to hold the draft state of the text input in the child component.

    Lowering the state to the child component can be a performance optimization to prevent the parent re-rendering when the input state changes. Otherwise the parent component will re-render every time there is a change in the text input.

    We’ll also pass down an example statement as a default value for each of the five notes we’ll write.

    Here’s a buggy way to do this:

    // These are going to be our default values for each of the five notes
    // To give the user an idea of what they might write
    const ideaList = ["I'm thankful for my friends",
                      "I'm thankful for my family",
                      "I'm thankful for my health",
                      "I'm thankful for my hobbies",
                      "I'm thankful for CSS Tricks Articles"]
    
    const maxStatements = 5;
    
    function Parent(){
      const [list,setList] = useState([]);
      
      // Handler function for when the statement is completed
      // Sets state providing a new array combining the current list and the new item 
      function onStatementComplete(payload){
        setList(list=>[...list,payload]);
      }
      // Function to reset the list back to an empty array
       function reset(){
        setList([]);
      }
      return <div>
        <h1>Your thankful list</h1>
        <p>A five point list of things you're thankful for:</p>
    
        {/* First we list the statements that have been completed*/}
        {list.map((item,index)=>{return <p>Item {index+1}: {item}</p>})}
    
        {/* If the length of the list is under our max statements length, we render 
        the statement form for the user to enter a new statement.
        We grab an example statement from the idealist and pass down the onStatementComplete function.
        Note: This implementation won't work as expected*/}
        {list.length<maxStatements ? 
          <StatementForm initialStatement={ideaList[list.length]} onStatementComplete={onStatementComplete}/>
          :<button onClick={reset}>Reset</button>
        }
      </div>;
    }
    
    // Our child StatementForm component This accepts the example statement for it's initial state and the on complete function
    function StatementForm({initialStatement,onStatementComplete}){
       // We hold the current state of the input, and set the default using initialStatement prop
     const [statement,setStatement] = useState(initialStatement);
    
      return <div>
        {/*On submit we prevent default and fire the onStatementComplete function received via props*/}
        <form onSubmit={(e)=>{e.preventDefault(); onStatementComplete(statement)}}>
        <label htmlFor="statement-input">What are you thankful for today?</label><br/>
        {/* Our controlled input below*/}
        <input id="statement-input" onChange={(e)=>setStatement(e.target.value)} value={statement} type="text"/>
        <input type="submit"/>
          </form>
      </div>
    }

    There’s a problem with this: each time we submit a completed statement, the input incorrectly holds onto the submitted note in the textbox. We want to replace it with an example statement from our list.

    Even though we’re passing down a different example string every time, the child remembers the old state and our newer prop is ignored. You could potentially check whether the props have changed on every render in a useEffect, and then reset the state if they have. But that can cause bugs when different parts of your data use the same values and you want to force the child state to reset even though the prop remains the same.

    The solution

    If you need a child component where the parent needs the ability to reset the child on demand, there is a way to do it: it’s by changing the key prop on the child.

    You might have seen this special key prop from when you’re rendering elements based on an array and React throws a warning asking you to provide a key for each element. Changing the key of a child element ensures React creates a brand new version of the element. It’s a way of telling React that you are rendering a conceptually different item using the same component.

    Let’s add a key prop to our child component. The value is the index we’re about to fill with our statement:

    <StatementForm key={list.length} initialStatement={ideaList[list.length]} onStatementComplte={onStatementComplete}/>

    Here’s what this looks like in our list app:

    Note the only thing that changed here is that the child component now has a key prop based on the array index we’re about to fill. Yet, the behavior of the component has completely changed.

    Now each time we submit and finish writing out statement, the old state in the child component gets thrown away and replaced with the example statement.

    Buggy code #3: Stale closure bugs

    This is a common issue with React hooks. There’s previously been a CSS-Tricks article about dealing with stale props and states in React’s functional components.

    Let’s take a look at a few situations where you might run into trouble. The first crops up is when using useEffect. If we’re doing anything asynchronous inside of useEffect we can get into trouble using old state or props.

    Here’s an example. We need to increment a count every second. We set it up on the first render with a useEffect, providing a closure that increments the count as the first argument, and an empty array as the second argument. We’ll give it the empty array as we don’t want React to restart the interval on every render.

    function Counter() { 
      let [count, setCount] = useState(0);
    
      useEffect(() => {
        let id = setInterval(() => {
          setCount(count + 1);
        }, 1000);
        return () => clearInterval(id);
      },[]);
    
      return <h1>{count}</h1>;
    }

    Oh no! The count gets incremented to 1 but never changes after that! Why is this happening?

    It’s to do with two things:

    Having a look at the MDN docs on closures, we can see:

    A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.

    The “lexical environment” in which our useEffect closure is declared is inside our Counter React component. The local variable we’re interested is count, which is zero at the time of the declaration (the first render).

    The problem is, this closure is never declared again. If the count is zero at the time declaration, it will always be zero. Each time the interval fires, it’s running a function that starts with a count of zero and increments it to 1.

    So how might we get the function declared again? This is where the second argument of the useEffect call comes in. We thought we were extremely clever only starting off the interval once by using the empty array, but in doing so we shot ourselves in the foot. If we had left out this argument, the closure inside useEffect would get declared again with a new count every time.

    The way I like to think about it is that the useEffect dependency array does two things:

    • It will fire the useEffect function when the dependency changes.
    • It will also redeclare the closure with the updated dependency, keeping the closure safe from stale state or props.

    In fact, there’s even a lint rule to keep your useEffect instances safe from stale state and props by making sure you add the right dependencies to the second argument.

    But we don’t actually want to reset our interval every time the component gets rendered either. How do we solve this problem then?

    The solution

    Again, there are multiple solutions to our problem here. Let’s start with the easiest: not using the count state at all and instead passing a function into our setState call:

    function Counter() { 
      let [count, setCount] = useState(0);
    
      useEffect(() => {
        let id = setInterval(() => {
          setCount(prevCount => prevCount+ 1);
        }, 1000);
        return () => clearInterval(id);
      },[]);
    
      return <h1>{count}</h1>;
    }

    That was easy. Another option is to use the useRef hook like this to keep a mutable reference of the count:

    function Counter() {
      let [count, setCount] = useState(0);
      const countRef = useRef(count)
      
      function updateCount(newCount){
        setCount(newCount);
        countRef.current = newCount;
      }
    
      useEffect(() => {
        let id = setInterval(() => {
          updateCount(countRef.current + 1);
        }, 1000);
        return () => clearInterval(id);
      },[]);
    
      return <h1>{count}</h1>;
    }
    
    ReactDOM.render(<Counter/>,document.getElementById("root"))

    To go more in depth on using intervals and hooks you can take a look at this article about creating a useInterval in React by Dan Abramov, who is one of the React core team members. He takes a different route where, instead of holding the count in a ref, he places the entire closure in a ref.

    To go more in depth on useEffect you can have a look at his post on useEffect.

    More stale closure bugs

    But stale closures won’t just appear in useEffect. They can also turn up in event handlers and other closures inside your React components. Let’s have a look at a React component with a stale event handler; we’ll create a scroll progress bar that does the following:

    • increases its width along the screen as the user scrolls
    • starts transparent and becomes more and more opaque as the user scrolls
    • provides the user with a button that randomizes the color of the scroll bar

    We’re going to leave the progress bar outside of the React tree and update it in the event handler. Here’s our buggy implementation:

    <body>
    <div id="root"></div>
    <div id="progress"></div>
    </body>
    function Scroller(){
    
      // We'll hold the scroll position in one state
      const [scrollPosition, setScrollPosition] = useState(window.scrollY);
      // And the current color in another
      const [color,setColor] = useState({r:200,g:100,b:100});
      
      // We assign out scroll listener on the first render
      useEffect(()=>{
       document.addEventListener("scroll",handleScroll);
        return ()=>{document.removeEventListener("scroll",handleScroll);}
      },[]);
      
      // A function to generate a random color. To make sure the contrast is strong enough
      // each value has a minimum value of 100
      function onColorChange(){
        setColor({r:100+Math.random()*155,g:100+Math.random()*155,b:100+Math.random()*155});
      }
      
      // This function gets called on the scroll event
      function handleScroll(e){
        // First we get the value of how far down we've scrolled
        const scrollDistance = document.body.scrollTop || document.documentElement.scrollTop;
        // Now we grab the height of the entire document
        const documentHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
         // And use these two values to figure out how far down the document we are
        const percentAlong =  (scrollDistance / documentHeight);
        // And use these two values to figure out how far down the document we are
        const progress = document.getElementById("progress");
        progress.style.width = `${percentAlong*100}%`;
        // Here's where our bug is. Resetting the color here will mean the color will always 
        // be using the original state and never get updated
        progress.style.backgroundColor = `rgba(${color.r},${color.g},${color.b},${percentAlong})`;
        setScrollPosition(percentAlong);
      }
      
      return <div className="scroller" style={{backgroundColor:`rgb(${color.r},${color.g},${color.b})`}}>
        <button onClick={onColorChange}>Change color</button>
        <span class="percent">{Math.round(scrollPosition* 100)}%</span>
      </div>
    }
    
    ReactDOM.render(<Scroller/>,document.getElementById("root"))

    Our bar gets wider and increasingly more opaque as the page scrolls. But if you click the change color button, our randomized colors are not affecting the progress bar. We’re getting this bug because the closure is affected by component state, and this closure is never being re-declared so we only get the original value of the state and no updates.

    You can see how setting up closures that call external APIs using React state, or component props might give you grief if you’re not careful.

    The solution

    Again, there are multiple ways to fix this problem. We could keep the color state in a mutable ref which we could later use in our event handler:

    const [color,setColor] = useState({r:200,g:100,b:100});
    const colorRef = useRef(color);
    
    function onColorChange(){
      const newColor = {r:100+Math.random()*155,g:100+Math.random()*155,b:100+Math.random()*155};
      setColor(newColor);
      colorRef.current=newColor;
      progress.style.backgroundColor = `rgba(${newColor.r},${newColor.g},${newColor.b},${scrollPosition})`;
    }

    This works well enough but it doesn’t feel ideal. You may need to write code like this if you’re dealing with third-party libraries and you can’t find a way to pull their API into your React tree. But by keeping one of our elements out of the React tree and updating it inside of our event handler, we’re swimming against the tide.

    This is a simple fix though, as we’re only dealing with the DOM API. An easy way to refactor this is to include the progress bar in our React tree and render it in JSX allowing it to reference the component’s state. Now we can use the event handling function purely for updating state.

    function Scroller(){
      const [scrollPosition, setScrollPosition] = useState(window.scrollY);
      const [color,setColor] = useState({r:200,g:100,b:100});  
    
      useEffect(()=>{
       document.addEventListener("scroll",handleScroll);
        return ()=>{document.removeEventListener("scroll",handleScroll);}
      },[]);
      
      function onColorChange(){
        const newColor = {r:100+Math.random()*155,g:100+Math.random()*155,b:100+Math.random()*155};
        setColor(newColor);
      }
    
      function handleScroll(e){
        const scrollDistance = document.body.scrollTop || document.documentElement.scrollTop;
        const documentHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
        const percentAlong =  (scrollDistance / documentHeight);
        setScrollPosition(percentAlong);
      }
      return <>
        <div class="progress" id="progress"
       style={{backgroundColor:`rgba(${color.r},${color.g},${color.b},${scrollPosition})`,width: `${scrollPosition*100}%`}}></div>
        <div className="scroller" style={{backgroundColor:`rgb(${color.r},${color.g},${color.b})`}}>
        <button onClick={onColorChange}>Change color</button>
        <span class="percent">{Math.round(scrollPosition * 100)}%</span>
      </div>
      </>
    }

    That feels better. Not only have we removed the chance for our event handler to get stale, we’ve also converted our progress bar into a self contained component which takes advantage of the declarative nature of React.

    Also, for a scroll indicator like this, you might not even need JavaScript — have take a look at the up-and-coming @scroll-timeline CSS function or an approach using a gradient from Chris’ book on the greatest CSS tricks!

    Wrapping up

    We’ve had a look at three different ways you can create bugs in your React applications and some ways to fix them. It can be easy to look at counter examples which follow a happy path and don’t show subtleties in the APIs that might cause problems.

    If you still find yourself needing to build a stronger mental model of what your React code is doing, here’s a list of resources which can help:


    Three Buggy React Code Examples and How to Fix Them originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/three-buggy-react-code-examples-and-how-to-fix-them/feed/ 0 345418
    Links on React and JavaScript https://css-tricks.com/links-on-react-and-javascript/ https://css-tricks.com/links-on-react-and-javascript/#respond Tue, 13 Jul 2021 19:57:10 +0000 https://css-tricks.com/?p=344316 As a day-job, React-using person, I like to stay abreast of interesting React news. As such, I save a healthy amount of links. Allow me to dump out my latest pile. Most of this is about React but not all …


    Links on React and JavaScript originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    As a day-job, React-using person, I like to stay abreast of interesting React news. As such, I save a healthy amount of links. Allow me to dump out my latest pile. Most of this is about React but not all of it.

    • JavaScript: What is the meaning of this? — Jake Archibald puts out the canonical article on this.
    • Human-Readable JavaScript: A Tale of Two Experts — Laurie Barth compares examples of code that do the same thing, but have different levels of readability. There isn’t always a straight answer “… but when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.”
    • petite-vue — jQuery was amazing and there is plenty of perfectly fine jQuery code, but the reason jQuery is a bit looked down upon these days is the messy code bases that were made with it. Some lessons were learned. While inline JavaScript handlers were once heavily scorned, nearly every popular JavaScript library today has brought them back. But let’s say something like React is too heavy-handed for you—what is the jQuery of light on-page interactivity stuff? Vue sort of walks the line between that and being more of a “big framework.” Alpine.js is probably the main player. But here comes Vue again with a poke at Alpine with a version of itself that is pretty darn small and does the same sort of stuff.

    Links on React and JavaScript originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    ]]>
    https://css-tricks.com/links-on-react-and-javascript/feed/ 0 344316