Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/artsy/fresnel

An SSR compatible approach to CSS media query based responsive layouts for React.
https://github.com/artsy/fresnel

media-queries nextjs react react-18 responsive ssr

Last synced: 6 days ago
JSON representation

An SSR compatible approach to CSS media query based responsive layouts for React.

Awesome Lists containing this project

README

        

# @artsy/fresnel

[![CircleCI][ci-icon]][ci] [![npm version][npm-icon]][npm]

> The Fresnel equations describe the reflection of light when incident on an
> interface between different optical media.

– https://en.wikipedia.org/wiki/Fresnel_equations

## Installation

```sh
# React 18+
yarn add @artsy/fresnel

# React 17
yarn add @artsy/fresnel@6
```

**Table of Contents**

- [Overview](#overview)
- [Basic Example](#basic-example)
- [Server-side Rendering (SSR)](#server-side-rendering-ssr-usage)
- [Usage with Next](#usage-with-gatsby-or-next)
- [Example Apps](#example-apps)
- [Why not conditionally render?](#why-not-conditionally-render)
- [API](#api)
- [Pros vs Cons](#pros-vs-cons)
- [Development](#development)

## Overview

When writing responsive components it's common to use media queries to adjust
the display when certain conditions are met. Historically this has taken place
directly in CSS/HTML:

```css
@media screen and (max-width: 767px) {
.my-container {
width: 100%;
}
}
@media screen and (min-width: 768px) {
.my-container {
width: 50%;
}
}
```

```html


```

By hooking into a breakpoint definition, `@artsy/fresnel` takes this declarative
approach and brings it into the React world.

## Basic Example

```tsx
import React from "react"
import ReactDOM from "react-dom"
import { createMedia } from "@artsy/fresnel"

const { MediaContextProvider, Media } = createMedia({
// breakpoints values can be either strings or integers
breakpoints: {
sm: 0,
md: 768,
lg: 1024,
xl: 1192,
},
})

const App = () => (











)

ReactDOM.render(, document.getElementById("react"))
```

## Server-side Rendering (SSR) Usage

The first important thing to note is that when server-rendering with
`@artsy/fresnel`, all breakpoints get rendered by the server. Each `Media`
component is wrapped by plain CSS that will only show that breakpoint if it
matches the user's current browser size. This means that the client can
accurately start rendering the HTML/CSS while it receives the markup, which is
long before the React application has booted. This improves perceived
performance for end-users.

Why not just render the one that the current device needs? We can't accurately
identify which breakpoint your device needs on the server. We could use a
library to sniff the browser user-agent, but those aren't always accurate, and
they wouldn't give us all the information we need to know when we are
server-rendering. Once client-side JS boots and React attaches, it simply washes
over the DOM and removes markup that is unneeded, via a `matchMedia` call.

### SSR Example

First, configure `@artsy/fresnel` in a `Media` file that can be shared across
the app:

```tsx
// Media.tsx

import { createMedia } from "@artsy/fresnel"

const ExampleAppMedia = createMedia({
breakpoints: {
sm: 0,
md: 768,
lg: 1024,
xl: 1192,
},
})

// Generate CSS to be injected into the head
export const mediaStyle = ExampleAppMedia.createMediaStyle()
export const { Media, MediaContextProvider } = ExampleAppMedia
```

Create a new `App` file which will be the launching point for our application:

```tsx
// App.tsx

import React from "react"
import { Media, MediaContextProvider } from "./Media"

export const App = () => {
return (

Hello mobile!
Hello desktop!

)
}
```

Mount `` on the client:

```tsx
// client.tsx

import React from "react"
import ReactDOM from "react-dom"
import { App } from "./App"

ReactDOM.render(, document.getElementById("react"))
```

Then on the server, setup SSR rendering and pass `mediaStyle` into a ``
tag in the header:

```tsx
// server.tsx

import React from "react"
import ReactDOMServer from "react-dom/server"
import express from "express"

import { App } from "./App"
import { mediaStyle } from "./Media"

const app = express()

app.get("/", (_req, res) => {
const html = ReactDOMServer.renderToString(<App />)

res.send(`
<html>
<head>
<title>@artsy/fresnel - SSR Example</title>

<!–– Inject the generated styles into the page head -->
<style type="text/css">${mediaStyle}


${html}