Стартовый шаблон Next.js + MUI5 + TypeScript

Очень часто при старте проекта необходимо выбрать набор инструментов, которые значительно упрощают разработку и, которые можно в дальнейшем использовать и в других проектах.

В

Вадим Пашаев

12/12/2021

Стартовый шаблон Next.js + MUI5 + TypeScript

И здесь встает вопрос, а будут ли они работать вместе должным образом.

Итак, какие задачи перед нами стоят:

  • Обеспечить стабильную сборку для быстрого создания новых проектов и прототипирования;
  • Обеспечить статическую типизацию и проверку типов для улучшения качества кода;
  • Перейти на новый Material UI 5, который претерпел очень много полезных изменений по сравнению с 4 версией (который для нашей студии уже стал стандартом, де-факто);

В этой статье мы соберем с вами сборку на Next.js + MUI5 + TypeScript.

Cвязка React+Typescript давно уже стала самой популярная для создания клиентских приложений. Их в своих проектах уже используют следующие бренды: Yandex, Тинькофф, ВК, Avito, Сбербанк, Контур, Netflix, Instagram, Pinterest, AirBNB и другие крупные компании.

Next.js

Next.js — это основанный на React фреймворк, предназначенный для разработки веб-приложений, обладающих функционалом, выходящим за рамки SPA, т.е. так называемых одностраничных приложений. Известно, что основным недостатком одностраничных приложений или SPA являются проблемы с индексацией страниц таких приложений поисковыми роботами, что негативно влияет на SEO.

TypeScript

TypeScript (далее TS) - это надстройка над JavaScript (далее JS), которая добавляет статическую типизацию и еще кучу полезных фишек в нативный JS, старается исправить большинство недочетов и предоставляет нам мощнейшие иструменты для улучшения качества кода. Про все достоинства и недостатки TS подробнее можно почитать в других статьях. Например, здесь.

Наиболее распространенными ошибками, совершаемыми JavaScript-разработчиками, являются ошибки вида, когда вместо ожидаемого значения используется неправильное. Это может быть связано с простыми опечатками, непониманием интерфейса используемой библиотеки, неверными предположениями о поведении кода во время выполнения и т.д. Цель TypeScript - осуществлять проверку типов значений, используемых в программе, написанной на JavaScript. TypeScript - это инструмент, который выполняет свою работу до запуска кода (поэтому его называют статическим) и обеспечивает использование приложением значений правильных типов.

О том, что дает нам TypeScript в React можно подробнее прочитать здесь.

MUI5 (MaterialUI 5)

MUI - это довольно надежная, настраиваемая и доступная библиотека базовых и расширенных компонентов, позволяющая создавать собственную систему дизайна и быстрее разрабатывать приложения на React.

Собираем шаблон

1. Установка Next.js и TypeScript

Для начала сборки нам потребуется создать шаблонный проект на Next.js + Typescript. Next.js поддерживает TypeScript из коробки, поэтому все, что нам нужно сделать на этом этапе, это набрать в консоли:

yarn create next-app --typescript

Где флаг --typescript как раз указывает на создание проекта с надстройкой TypeScript.

После запуска команды вам предложат задать имя для проекта, которое и будет совпадать с именем папки, в которой создастся проект.

2. Установка MUI

С установкой MaterialUI-5 все немного сложнее ;)

Для начала в проект устанавливаем следующие пакеты:

yarn add @emotion/cache @emotion/react @emotion/server @emotion/styled @mui/icons-material @mui/material

3. Редактируем файл _app.js

Next.js использует компонент App для инициализации страниц, поэтому вы можете переопределить поведение по умолчанию, который позволит использовать очень интересные вещи:

  1. Сохранение частей макета при переключении между страницами;
  2. Сохранение состояния при навигации по страницам;
  3. Пользовательская обработка ошибок;
  4. Встраивание дополнительных данных на страницу;
  5. Добавление глобальных CSS-стилей.

Что нужно сделать?

Идем в документацию Next.js и открываем раздел Advanced Features/Custom App. Копируем оттуда весь код и создаем файл _app.js в папке /pages

// import App from 'next/app' function MyApp({ Component, pageProps }) { return <Component {...pageProps} /> } // Only uncomment this method if you have blocking data requirements for // every single page in your application. This disables the ability to // perform automatic static optimization, causing every page in your app to // be server-side rendered. // // MyApp.getInitialProps = async (appContext) => { // // calls page's `getInitialProps` and fills `appProps.pageProps` // const appProps = await App.getInitialProps(appContext); // // return { ...appProps } // } export default MyApp

После этого необходимо добавить несколько изменений в файл _app.js

import Head from 'next/head' import type { AppProps } from 'next/app' import { ThemeProvider } from '@mui/material/styles' import { CssBaseline } from '@mui/material' import { CacheProvider, EmotionCache } from '@emotion/react' import theme from '../styles/theme' import createEmotionCache from '../utils/createEmotionCache' const clientSideEmotionCache = createEmotionCache() interface MyAppProps extends AppProps { emotionCache?: EmotionCache } export default function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: MyAppProps) { return ( <CacheProvider value={emotionCache}> <Head> <title>Change title in _app.tsx</title> <meta name="viewport" content='initial-scale=1, width=device-width' /> </Head> <ThemeProvider theme={theme}> <CssBaseline /> <Component {...pageProps} /> </ThemeProvider> </CacheProvider> ) }

4. Добавляем файл _document.js

Файл _document.js в Next.js обычно используется для дополнения тегов <html> и <body> вашего приложения. Это необходимо, потому, что страницы Next.js пропускают определение разметки окружающего документа.

Создаем в папке /pages файл _document.js. И вписываем сюда следующий код.

import React from 'react' import Document, { Html, Head, NextScript, Main } from "next/document" import createEmotionServer from "@emotion/server/create-instance" import theme from '../styles/theme' import createEmotionCache from "../utils/createEmotionCache" export default class MyDocument extends Document { render() { return ( <Html lang="ru"> <Head> <meta name="theme-color" content={theme.palette.primary.main} /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> </Head> <body> <Main /> <NextScript /> </body> </Html> ) } } MyDocument.getInitialProps = async (ctx) => { const originalRenderPage = ctx.renderPage const cache = createEmotionCache() const { extractCriticalToChunks } = createEmotionServer(cache) ctx.renderPage = () => originalRenderPage({ enhanceApp: (App: any) => function EnhanceApp(props) { return <App emotionCache={cache} {...props} /> } }) const initialProps = await Document.getInitialProps(ctx) const emotionStyles = extractCriticalToChunks(initialProps.html) const emotionStyleTags = emotionStyles.styles.map(style => ( <style data-emotion={`${style.key} ${style.ids.join(' ')}`} key={style.key} dangerouslySetInnerHTML={{ __html: style.css }} /> )) return { ...initialProps, styles: [...React.Children.toArray(initialProps.styles), ...emotionStyleTags] } }

5. Пишем первую страницу на MaterialUI 5

import { Link, Typography } from '@mui/material' import { NextPage } from 'next' import NextLink from 'next/link' import Layout from '../components/Layout' const HomePage: NextPage = () => { return ( <Layout title="Главная | Next.js + TypeScript Example"> <Typography component="h1" variant='h1'>Привет, Next.js 👋</Typography> <Typography> <NextLink href="/about" passHref> <Link>О нас</Link> </NextLink> </Typography> </Layout> ) } export default HomePage

Все на этом туториал завершен. Код можно найти на Github