_document.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import Document, {
  2. DocumentContext,
  3. DocumentProps,
  4. Head,
  5. Html,
  6. Main,
  7. NextScript,
  8. } from "next/document";
  9. import React from "react";
  10. import createEmotionServer from "@emotion/server/create-instance";
  11. import { EnteAppProps } from "@ente/shared/apps/types";
  12. import createEmotionCache from "@ente/shared/themes/createEmotionCache";
  13. import { AppType } from "next/app";
  14. export interface EnteDocumentProps extends DocumentProps {
  15. emotionStyleTags: JSX.Element[];
  16. }
  17. export default function EnteDocument({ emotionStyleTags }: EnteDocumentProps) {
  18. return (
  19. <Html lang="en">
  20. <Head>
  21. <meta
  22. name="description"
  23. content="ente - end-to-end encrypted cloud with open-source apps"
  24. />
  25. <link rel="icon" href="/images/favicon.png" type="image/png" />
  26. <link rel="icon" type="image/png" href="/images/favicon.png" />
  27. <meta name="apple-mobile-web-app-capable" content="yes" />
  28. {emotionStyleTags}
  29. </Head>
  30. <body>
  31. <Main />
  32. <NextScript />
  33. </body>
  34. </Html>
  35. );
  36. }
  37. // `getInitialProps` belongs to `_document` (instead of `_app`),
  38. // it's compatible with static-site generation (SSG).
  39. EnteDocument.getInitialProps = async (ctx: DocumentContext) => {
  40. // Resolution order
  41. //
  42. // On the server:
  43. // 1. app.getInitialProps
  44. // 2. page.getInitialProps
  45. // 3. document.getInitialProps
  46. // 4. app.render
  47. // 5. page.render
  48. // 6. document.render
  49. //
  50. // On the server with error:
  51. // 1. document.getInitialProps
  52. // 2. app.render
  53. // 3. page.render
  54. // 4. document.render
  55. //
  56. // On the client
  57. // 1. app.getInitialProps
  58. // 2. page.getInitialProps
  59. // 3. app.render
  60. // 4. page.render
  61. const originalRenderPage = ctx.renderPage;
  62. // You can consider sharing the same Emotion cache between all the SSR requests to speed up performance.
  63. // However, be aware that it can have global side effects.
  64. const cache = createEmotionCache();
  65. // eslint-disable-next-line @typescript-eslint/unbound-method
  66. const { extractCriticalToChunks } = createEmotionServer(cache);
  67. ctx.renderPage = () =>
  68. originalRenderPage({
  69. enhanceApp: (
  70. App: React.ComponentType<
  71. React.ComponentProps<AppType> & EnteAppProps
  72. >,
  73. ) =>
  74. function EnhanceApp(props) {
  75. return <App emotionCache={cache} {...props} />;
  76. },
  77. });
  78. const initialProps = await Document.getInitialProps(ctx);
  79. // This is important. It prevents Emotion to render invalid HTML.
  80. // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
  81. const emotionStyles = extractCriticalToChunks(initialProps.html);
  82. const emotionStyleTags = emotionStyles.styles.map((style) => (
  83. <style
  84. data-emotion={`${style.key} ${style.ids.join(" ")}`}
  85. key={style.key}
  86. // eslint-disable-next-line react/no-danger
  87. dangerouslySetInnerHTML={{ __html: style.css }}
  88. />
  89. ));
  90. return {
  91. ...initialProps,
  92. emotionStyleTags,
  93. };
  94. };