├── .gitignore
├── .prettierrc
├── README.md
├── components
├── CriticalCssHead
│ ├── InlineStyle.tsx
│ └── index.tsx
└── Example
│ ├── Example.module.css
│ └── index.tsx
├── next-env.d.ts
├── package.json
├── pages
├── _app.tsx
├── _document.tsx
└── index.tsx
├── postcss.config.js
├── styles
└── global.css
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .next
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "printWidth": 120,
4 | "proseWrap": "always",
5 | "singleQuote": true,
6 | "trailingComma": "all"
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js 9.3, TypeScript, tailwindcss, Critical CSS
2 |
3 | A [Next.js](https://nextjs.org/) 9.3 Starter Template with [TypeScript](https://www.typescriptlang.org/),
4 | [tailwindcss](https://tailwindcss.com/), and the [Critical CSS technique](https://web.dev/extract-critical-css/).
5 |
6 | ## Features
7 |
8 | ✅ Next.js 9.3.0
✅ tailwindcss 1.2.0
✅ Strict TypeScript 3.8.3
✅ Remove Unused CSS
✅ Inline Critical CSS in the `
`
13 |
14 | ## Production HTML
15 |
16 | The vast majority of the CSS is [normalize.css](https://necolas.github.io/normalize.css/) from `@tailwind base;` in
17 | `/styles/global.css`. Most projects normalise CSS, but you do you.
18 |
19 | ```html
20 |
21 |
22 |
23 |
24 |
25 |
26 |
337 |
343 |
344 |
345 |
346 |
347 |
352 |
353 |
354 |
355 |
356 |
357 |
Hello Next.js 9.3
358 |
With TypeScript, tailwindcss, and Critical CSS
359 |
360 |
361 |
372 |
373 |
374 |
375 |
376 |
377 |
381 |
382 |
383 |
384 |
385 |
386 | ```
387 |
--------------------------------------------------------------------------------
/components/CriticalCssHead/InlineStyle.tsx:
--------------------------------------------------------------------------------
1 | import { readFileSync } from 'fs';
2 | import { join } from 'path';
3 |
4 | export interface Props {
5 | assetPrefix?: string;
6 | file: string;
7 | nonce?: string;
8 | }
9 |
10 | export const InlineStyle: React.FC = ({ assetPrefix, file, nonce }) => {
11 | const cssPath = join(process.cwd(), '.next', file);
12 | const cssSource = readFileSync(cssPath, 'utf-8');
13 | const html = { __html: cssSource };
14 | const id = `${assetPrefix}/_next/${file}`;
15 | return ;
16 | };
17 |
--------------------------------------------------------------------------------
/components/CriticalCssHead/index.tsx:
--------------------------------------------------------------------------------
1 | import { Head } from 'next/document';
2 | import { InlineStyle } from './InlineStyle';
3 |
4 | type DocumentFiles = {
5 | sharedFiles: string[];
6 | pageFiles: string[];
7 | allFiles: string[];
8 | };
9 |
10 | export class CriticalCssHead extends Head {
11 | getCssLinks({ allFiles }: DocumentFiles) {
12 | const { assetPrefix } = this.context._documentProps;
13 | const { nonce } = this.props;
14 | const isCss = (file: string): boolean => /\.css$/.test(file);
15 | const renderCss = (file: string) => ;
16 | return allFiles && allFiles.length > 0 ? allFiles.filter(isCss).map(renderCss) : null;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/components/Example/Example.module.css:
--------------------------------------------------------------------------------
1 | .example {
2 | @apply font-bold;
3 | @apply text-6xl;
4 | }
5 |
--------------------------------------------------------------------------------
/components/Example/index.tsx:
--------------------------------------------------------------------------------
1 | import css from './Example.module.css';
2 |
3 | export const Example: React.FC = () => (
4 |
5 |
Hello Next.js 9.3
6 |
With TypeScript, TailwindCSS, and inlined Critical CSS
7 |
8 | );
9 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-ts-tailwind",
3 | "description": "Next.js 9.3, TypeScript, TailwindCSS, Critical CSS",
4 | "version": "0.0.0",
5 | "author": "Jamie Mason (https://github.com/JamieMason)",
6 | "dependencies": {
7 | "next": "9.3.0",
8 | "react": "16.13.0",
9 | "react-dom": "16.13.0"
10 | },
11 | "devDependencies": {
12 | "@fullhuman/postcss-purgecss": "2.1.0",
13 | "@types/node": "13.9.0",
14 | "@types/react": "16.9.23",
15 | "postcss-preset-env": "6.7.0",
16 | "tailwindcss": "1.2.0",
17 | "typescript": "3.8.3"
18 | },
19 | "keywords": [],
20 | "license": "MIT",
21 | "main": "index.js",
22 | "scripts": {
23 | "build": "next build",
24 | "dev": "next",
25 | "start": "next start"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { NextPage } from 'next';
2 | import { AppProps } from 'next/app';
3 | import '../styles/global.css';
4 |
5 | const App: NextPage = ({ Component, pageProps }) => {
6 | return ;
7 | };
8 |
9 | export default App;
10 |
--------------------------------------------------------------------------------
/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, { Main, NextScript } from 'next/document';
2 | import { CriticalCssHead } from '../components/CriticalCssHead';
3 |
4 | class ExtendedNextDocument extends Document {
5 | render() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
18 | export default ExtendedNextDocument;
19 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { NextPage } from 'next';
2 | import { Example } from '../components/Example';
3 |
4 | const Home: NextPage = () => ;
5 |
6 | export default Home;
7 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | 'tailwindcss',
4 | process.env.NODE_ENV === 'production'
5 | ? [
6 | '@fullhuman/postcss-purgecss',
7 | {
8 | content: ['./pages/**/*.{js,jsx,ts,tsx}', './components/**/*.{js,jsx,ts,tsx}'],
9 | defaultExtractor: content => content.match(/[\w-/:]+(?