<onWebFocus />

Knowledge is only real when shared.

All-in-JS

August 16, 2021

Exploring the consequences of JSX and CSS-in-JS.

Traditionally a website consists of an HTML file, some CSS files and optionally a few JavaScript files for each URL. The HTML and CSS remained mostly static during interactions unless a full rerender took place on the server after a switch to another URL or the submission of some data.

In recent years however this approach has been upended somewhat by frameworks which generate all or most of the HTML and CSS after the pageload on the client from within the JavaScript code. In the following we want to explore these approaches. Especially the approach JSX and CSS-in-JS where there is no clear separation of HTML, CSS and JS anymore.

HTML → JSX

React introduced an innovative concept called JSX where HTML-Tags can be written inside regular JavaScript code. Unlike before, snippets can be created like any other values without the need to call a function.

import { render } from 'react-dom'
      
render(() => (
  <div>
    <h1>Hello World</h1>
    <p>How are you doing?</p>
  </div>
))

Of course adding tags isn't valid JavaScript code, so the code first needs to be transpiled by transforming the tags to createElement function calls. However, all this happens automatically and usually frictionless in the background.

The technology was first developed at Facebook to be used with PHP on the server-side where HTML was traditionally rendered. As soon discovered, the technology was much more useful in the browser. There existing approaches to manipulate HTML that was previously rendered on the server were much less practical.

JSX is used with the most popular Frontend framework React. Other popular frameworks like Vue and Svelte use an approach where HTML is also placed in each JavaScript file that renders something. The difference is that HTML and JavaScript each have their distinct place in the file. Angular is a bit different as it encourages to create separate HTML and CSS files per component and import those. These latter mentioned frameworks add a custom template syntax to the HTML that will be transpiled at compile time so that it can render the data generated by the JavaScript code.

<script>
export default {
  data() {
    return {
      greeting: 'Hello World!'
    }
  }
}
</script>

<template>
  <p>{{ greeting }}</p>
</template>

This approach has made it very easy to add interactivity to any site.

CSS → CSS-in-JS

Placing CSS inside JavaScript isn't as flexible as adding HTML with JSX. Usually it's either done by placing the styles on Objects and processing them through a library before assigning to a tag or by writing CSS inside string templates that will then be processed.

import { css } from '@emotion/react'

const RedSquare = (
  <div css={css`
    background: red;
    height: 20px;
    width: 20px;
  `} />
)

The great thing comes from being inside JavaScript. This means you can generate styles from arbitrary logic.

import styled from '@emotion/styled'

const CustomSquare = styled.div<{ bold?: boolean }>`
  font-weight: ${({ bold = false }) => (bold ? 'bold' : '')};
`

Conclusion

Usually when working on a component you will edit the markup, along with the styles and also the logic. Allowing all these parts to be in the same place and in the same file will avoid unnecessary switching between files and makes it easier to keep an overview of the current piece the developer is working on. In some cases splitting the different parts up can make one part harder to grasp quickly. If the markup is separated an all together it becomes easier to find a certain part. So when writing everything in JavaScript that should be kept in mind and the parts placed so that they remain easy to read and it doesn't become a huge puzzle that has to be put together first.

It looks like this approach will stay popular and frameworks requiring separation of these concerns will keep being a thing of the past.