Skip to content

Latest commit

 

History

History

blitz-emotion-typescript

Twin + Blitz.js + Emotion + TypeScript

TwinTwinBlitzEmotionTypeScript

This example uses Blitz.js to build a Next.js App written in TypeScript and styled with Twin + emotion.

Download this example using degit

npx degit https://github.com/ben-rogerson/twin.examples/blitz-emotion-typescript app-name

From within the new folder, run npm install, then npm run dev to start the dev server.

Getting started

Installation

Install Blitz.js:

npx blitz new app-name

Install the dependencies

npm install @emotion/react @emotion/styled @emotion/server
npm install -D twin.macro tailwindcss babel-plugin-macros @emotion/babel-preset-css-prop
Install with Yarn
yarn add @emotion/react @emotion/styled @emotion/server
yarn add twin.macro tailwindcss babel-plugin-macros @emotion/babel-preset-css-prop -D

Add the global styles

Twin uses the same preflight base styles as Tailwind to smooth over cross-browser inconsistencies.

The GlobalStyles import adds these base styles, some @keyframes for animations, and some global css variables.

Import GlobalStyles within a new file placed in app/styles/GlobalStyles.tsx:

// app/styles/GlobalStyles.tsx
import { Fragment } from 'react'
import { Global } from '@emotion/react'
import tw, { css, theme, GlobalStyles as BaseStyles } from 'twin.macro'

const customStyles = css({
  body: {
    WebkitTapHighlightColor: theme`colors.purple.500`,
    ...tw`antialiased`,
  },
})

const GlobalStyles = () => (
  <Fragment>
    <BaseStyles />
    <Global styles={customStyles} />
  </Fragment>
)

export default GlobalStyles

Then import the GlobalStyles file in app/pages/_app.tsx:

// app/pages/_app.tsx
import {
  AppProps,
  ErrorBoundary,
  ErrorComponent,
  ErrorFallbackProps,
  useQueryErrorResetBoundary,
} from 'blitz'
+ import { Fragment } from 'react'
+ import GlobalStyles from './../styles/GlobalStyles'

export default function App({ Component, pageProps }: AppProps) {
  const getLayout = Component.getLayout || (page => page)

  return (
    <ErrorBoundary
      FallbackComponent={RootErrorFallback}
      onReset={useQueryErrorResetBoundary().reset}
    >
      {getLayout(
+        <Fragment>
+          <GlobalStyles />
          <Component {...pageProps} />
+        </Fragment>,
      )}
    </ErrorBoundary>
  )
}

function RootErrorFallback({ error }: ErrorFallbackProps) {
  return (
    <ErrorComponent
      statusCode={error.statusCode || 400}
      title={error.message || error.name}
    />
  )
}

SSR styles setup

Creating a _document.js file like this will put critical styles in the head of the page. Without this step, you’ll notice a difference between the SSR generated styles and the ones that hydrate on the client side.

// pages/_document.js
import { Fragment } from 'react'
import {
  Document,
  Html,
  DocumentHead,
  Main,
  BlitzScript /*DocumentContext*/,
} from 'blitz'
import { extractCritical } from '@emotion/server'

class MyDocument extends Document {
  static async getInitialProps(ctx: any) {
    const initialProps = await Document.getInitialProps(ctx)
    const critical = extractCritical(initialProps.html)
    initialProps.html = critical.html
    initialProps.styles = (
      <Fragment>
        {initialProps.styles}
        <style
          data-emotion-css={critical.ids.join(' ')}
          dangerouslySetInnerHTML={{ __html: critical.css }}
        />
      </Fragment>
    )

    return initialProps
  }

  render() {
    return (
      <Html lang="en">
        <DocumentHead />
        <body>
          <Main />
          <BlitzScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

Add the twin config (optional)

Twin’s config can be added in a couple of different files.

a) Either in babel-plugin-macros.config.js:

// babel-plugin-macros.config.js
module.exports = {
  twin: {
    preset: 'emotion',
  },
}

b) Or in package.json:

// package.json
"babelMacros": {
  "twin": {
    "preset": "emotion"
  }
},

Note: The preset gets set to 'emotion' by default, so adding the config is only useful if you want to adjust Twin’s other options.

Add the babel config

Add this config to your babel.config.js:

// babel.config.js
module.exports = {
  presets: ['blitz/babel', '@emotion/babel-preset-css-prop'],
  plugins: [
    '@emotion/babel-plugin',
    'babel-plugin-twin', // Optional
    'babel-plugin-macros',
  ],
}

Complete the TypeScript setup

To avoid red squiggly underlines, you’ll need to add the remaining types for your chosen css-in-js framework.

First up, you’ll need to install some types for React:

Then create a file in types/twin.d.ts and add these declarations:

// types/twin.d.ts
import 'twin.macro'
import styledImport from '@emotion/styled'
import { css as cssImport } from '@emotion/react'
import { CSSInterpolation } from '@emotion/serialize'

declare module 'twin.macro' {
  // The styled and css imports
  const styled: typeof styledImport
  const css: typeof cssImport
}

declare module 'react' {
  // The css prop
  interface HTMLAttributes<T> extends DOMAttributes<T> {
    css?: CSSInterpolation
  }
  // The inline svg css prop
  interface SVGProps<T> extends SVGProps<SVGSVGElement> {
    css?: CSSInterpolation
  }
}

Then add the following to your tsconfig.json:

// tsconfig.json
{
  "compilerOptions": {
    "jsxImportSource": "@emotion/react"
  },
  "include": ["types"]
}

Customization

Next steps

Learn how to work with twin

Learn more about emotion