Skip to content

Commit

Permalink
chore: adds theme-ui example project (#3637)
Browse files Browse the repository at this point in the history
  • Loading branch information
guatedude2 authored Jul 5, 2022
1 parent dbdecca commit f59f16c
Show file tree
Hide file tree
Showing 18 changed files with 321 additions and 0 deletions.
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,4 @@
- zachdtaylor
- zainfathoni
- zhe
- guatedude2
3 changes: 3 additions & 0 deletions examples/theme-ui/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"],
};
6 changes: 6 additions & 0 deletions examples/theme-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/.cache
/build
/public/build
.env
49 changes: 49 additions & 0 deletions examples/theme-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Example app with [theme-ui](https://theme-ui.com/)

This example features how to use [theme-ui](https://theme-ui.com/) with Remix.

## How this implementation works

Since [theme-ui](https://theme-ui.com/) is derived from [emotion](https://emotion.sh/), this example shows how we can leverage Emotion's server-side-caching to enable [theme-ui](https://theme-ui.com/) server side rendering.

*This implementation was based off [Saas-UI Remix guide](https://www.saas-ui.dev/docs/core/installation/remix-guide).*

### Theme-UI Related files

```
- app/
- styles/ - context.tsx - createEmotionCache.tsx
- entry.client.tsx
- entry.server.tsx
- root.tsx
```

1. `context.tsx` - Creates the server and client context.
2. `createEmotionCache.ts` - Create an instance of [Emotion cache](https://emotion.sh/docs/@emotion/cache).
4. `entry.client.tsx` - Consumes the emotion cache generated by the server and creates a provider that is then passed on the the app.
5. `entry.server.tsx` - Create the markup with the styles injected to serve on the server response.


## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/routes/index.tsx`. The page auto-updates as you edit the file.

## Commands

- `dev`: runs your application on `localhost:3000`
- `build`: creates the production build version
- `start`: starts a simple server with the build production code

## Related Links

[Theme-UI](https://theme-ui.com/)
Expand Down
32 changes: 32 additions & 0 deletions examples/theme-ui/app/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useState } from 'react';
import { hydrate } from 'react-dom';
import { CacheProvider } from '@emotion/react';
import { RemixBrowser } from '@remix-run/react';

import { ClientStyleContext } from './styles/context';
import createEmotionCache from './styles/createEmotionCache';

interface ClientCacheProviderProps {
children: React.ReactNode;
}

function ClientCacheProvider({ children }: ClientCacheProviderProps) {
const [cache, setCache] = useState(createEmotionCache());

function reset() {
setCache(createEmotionCache());
}

return (
<ClientStyleContext.Provider value={{ reset }}>
<CacheProvider value={cache}>{children}</CacheProvider>
</ClientStyleContext.Provider>
);
}

hydrate(
<ClientCacheProvider>
<RemixBrowser />
</ClientCacheProvider>,
document,
);
38 changes: 38 additions & 0 deletions examples/theme-ui/app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { renderToString } from 'react-dom/server';
import { CacheProvider } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';
import { RemixServer } from '@remix-run/react';
import type { EntryContext } from '@remix-run/node'; // Depends on the runtime you choose

import { ServerStyleContext } from './styles/context';
import createEmotionCache from './styles/createEmotionCache';

export default function handleRequest(request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext) {
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);

const html = renderToString(
<ServerStyleContext.Provider value={null}>
<CacheProvider value={cache}>
<RemixServer context={remixContext} url={request.url} />
</CacheProvider>
</ServerStyleContext.Provider>
);

const chunks = extractCriticalToChunks(html);

const markup = renderToString(
<ServerStyleContext.Provider value={chunks.styles}>
<CacheProvider value={cache}>
<RemixServer context={remixContext} url={request.url} />
</CacheProvider>
</ServerStyleContext.Provider>
);

responseHeaders.set('Content-Type', 'text/html');

return new Response(`<!DOCTYPE html>${markup}`, {
status: responseStatusCode,
headers: responseHeaders,
});
}
64 changes: 64 additions & 0 deletions examples/theme-ui/app/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useContext, useEffect } from 'react';
import { withEmotionCache } from '@emotion/react';
import { ThemeProvider } from '@theme-ui/core';
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
import type { MetaFunction, LinksFunction } from '@remix-run/node'; // Depends on the runtime you choose

import { ServerStyleContext, ClientStyleContext } from './styles/context';

export const meta: MetaFunction = () => ({
charset: 'utf-8',
title: 'New Remix App',
viewport: 'width=device-width,initial-scale=1',
});

interface DocumentProps {
children: React.ReactNode;
}

const Document = withEmotionCache(({ children }: DocumentProps, emotionCache) => {
const serverStyleData = useContext(ServerStyleContext);
const clientStyleData = useContext(ClientStyleContext);

// Only executed on client
useEffect(() => {
// re-link sheet container
emotionCache.sheet.container = document.head;
// re-inject tags
const tags = emotionCache.sheet.tags;
emotionCache.sheet.flush();
tags.forEach((tag) => {
(emotionCache.sheet as any)._insertTag(tag);
});
// reset cache to reapply global styles
clientStyleData?.reset();
}, []);

return (
<html lang="en">
<head>
<Meta />
<Links />
{serverStyleData?.map(({ key, ids, css }) => (
<style key={key} data-emotion={`${key} ${ids.join(' ')}`} dangerouslySetInnerHTML={{ __html: css }} />
))}
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
});

export default function App() {
return (
<Document>
<ThemeProvider theme={{ colors: { primary: '#33e' } }}>
<Outlet />
</ThemeProvider>
</Document>
);
}
21 changes: 21 additions & 0 deletions examples/theme-ui/app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @jsx jsx */
import React from 'react';
import {jsx} from '@theme-ui/core';
import { Link } from "@remix-run/react";


export default function Index() {
return (
<div sx={{backgroundColor: 'primary'}}>
<h1>Welcome to Remix with Emotion Example</h1>
<ul>
<li>
<Link to="/jokes">Jokes</Link>
</li>
<li>
<Link to="/jokes-error">Jokes: Error</Link>
</li>
</ul>
</div>
);
}
3 changes: 3 additions & 0 deletions examples/theme-ui/app/routes/jokes-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function JokesError() {
throw new Error("This route is no joking with us.");
}
14 changes: 14 additions & 0 deletions examples/theme-ui/app/routes/jokes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @jsx jsx */
import React from 'react';
import { jsx } from '@theme-ui/core';
import { Link } from "@remix-run/react";

export default function Jokes() {
return (
<div sx={{backgroundColor: 'primary'}}>
<h1>Jokes</h1>
<p>This route works fine.</p>
<Link to="/">Back to home</Link>
</div>
);
}
15 changes: 15 additions & 0 deletions examples/theme-ui/app/styles/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { createContext } from 'react';

export interface ServerStyleContextData {
key: string;
ids: Array<string>;
css: string;
}

export const ServerStyleContext = createContext<ServerStyleContextData[] | null>(null);

export interface ClientStyleContextData {
reset: () => void;
}

export const ClientStyleContext = createContext<ClientStyleContextData | null>(null);
5 changes: 5 additions & 0 deletions examples/theme-ui/app/styles/createEmotionCache.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import createCache from '@emotion/cache';

export default function createEmotionCache() {
return createCache({ key: 'css' });
}
31 changes: 31 additions & 0 deletions examples/theme-ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"private": true,
"sideEffects": false,
"scripts": {
"build": "remix build",
"dev": "remix dev",
"start": "remix-serve build"
},
"dependencies": {
"@emotion/cache": "^11.7.1",
"@emotion/react": "^11.8.1",
"@emotion/server": "^11.4.0",
"@remix-run/node": "1.6.2",
"@remix-run/react": "1.6.2",
"@remix-run/serve": "1.6.2",
"@theme-ui/core": "^0.14.6",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@remix-run/dev": "1.6.2",
"@remix-run/eslint-config": "1.6.2",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.13",
"eslint": "^8.10.0",
"typescript": "^4.6.2"
},
"engines": {
"node": ">=14"
}
}
Binary file added examples/theme-ui/public/favicon.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions examples/theme-ui/remix.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
ignoredRouteFiles: ["**/.*"],
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
};
2 changes: 2 additions & 0 deletions examples/theme-ui/remix.env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/node/globals" />
6 changes: 6 additions & 0 deletions examples/theme-ui/sandbox.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"hardReloadOnChange": true,
"container": {
"port": 3000
}
}
23 changes: 23 additions & 0 deletions examples/theme-ui/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"jsxImportSource": "@theme-ui/core",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ES2019",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},

// Remix takes care of building everything in `remix build`.
"noEmit": true
}
}

0 comments on commit f59f16c

Please sign in to comment.