npm i
npx husky install
. Automatic linting should occur when you commit!npm run dev
: Runs the local Next.js dev server.npm run build
: Generates the Next.js production build.npm start
: Starts the Next.js production build.Note that the actual commits in this repo do not exactly reflect these steps in the same order
npx create-next-app --ts
Add .env
to .gitignore
.
Add "baseUrl": "."
to "compilerOptions"
in tsconfig.json
Add to .eslintrc.json
:
"rules": {
"eqeqeq": "error",
"no-case-declarations": "error"
}
In folder .vscode
, create settings.json
:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
And extensions.json
:
{
"recommendations": ["dbaeumer.vscode-eslint"]
}
npm i -D @typescript-eslint/eslint-plugin
, add "plugin:@typescript-eslint/recommended"
to "extends"
in .eslintrc.json
npm i -D prettier eslint-config-prettier
, add "prettier"
to "extends"
in .eslintrc.json
Add to settings.json
:
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
Add "esbenp.prettier-vscode"
to extensions.json
Create .prettierrc.json
:
{
"singleQuote": true,
"trailingComma": "all"
}
Create .prettierignore
:
.next
npm i -D husky lint-staged
, npx husky install
, npx husky add .husky/pre-commit "npx lint-staged"
Create lint-staged.config.js
:
module.exports = {
'**/*.(ts|tsx)': () => 'npx tsc --noEmit',
'**/*.(ts|tsx|js)': (filenames) => [
`npx eslint --fix ${filenames.join(' ')}`,
`npx prettier --write ${filenames.join(' ')}`,
],
'**/*.(md|json)': (filenames) =>
`npx prettier --write ${filenames.join(' ')}`,
};
npm i styled-components
, npm i -D twin.macro tailwindcss babel-plugin-macros @types/styled-components
Create components/GlobalStyles.tsx
:
import { createGlobalStyle } from 'styled-components';
import { GlobalStyles as BaseStyles } from 'twin.macro';
const CustomStyles = createGlobalStyle`
body {}
`;
const GlobalStyles = () => (
<>
<BaseStyles />
<CustomStyles />
</>
);
export default GlobalStyles;
Modify pages/_app.tsx
:
import GlobalStyles from 'components/GlobalStyles';
import type { AppProps } from 'next/app';
const App = ({ Component, pageProps }: AppProps) => (
<div>
<GlobalStyles />
<Component {...pageProps} />
</div>
);
export default App;
Create babel-plugin-macros.config.js
:
module.exports = {
twin: {
preset: 'styled-components',
autoCssProp: false,
},
};
Create .babelrc.js
:
module.exports = {
presets: [['next/babel', { 'preset-react': { runtime: 'automatic' } }]],
plugins: [
'babel-plugin-macros',
['babel-plugin-styled-components', { ssr: true }],
],
};
Create _document.tsx
:
import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
Typescript does not like twin.macro and styled-components together for some reason, I searched a bit and it seems like creating types/twin.d.ts
solves the problem (IDK why, from here):
import 'twin.macro';
import styledImport, { CSSProp, css as cssImport } from 'styled-components';
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?: CSSProp;
tw?: string;
}
// The inline svg css prop
interface SVGProps<T> extends SVGProps<SVGSVGElement> {
css?: CSSProp;
tw?: string;
}
}
// The 'as' prop on styled components
declare global {
namespace JSX {
interface IntrinsicAttributes<T> extends DOMAttributes<T> {
as?: string;
}
}
}
Removed styles/
for our own styling system!