Wiki

Wiki

Here, we will discuss the problems and solutions that we have encountered in the development process.

Support for TypeScript 5.x in Create React App

In the https://github.com/1chooo/1chooo.com/pull/76, when I tried to upgrade the react-scripts to 5.x, I got the following error:

npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: react-scripts@5.0.1
npm error Found: typescript@5.5.4
npm error node_modules/typescript
npm error   typescript@"^5.5.4" from the root project
npm error   peer typescript@">= 2.7" from fork-ts-checker-webpack-plugin@6.5.3
npm error   node_modules/fork-ts-checker-webpack-plugin
npm error     fork-ts-checker-webpack-plugin@"^6.5.0" from react-dev-utils@12.0.1
npm error     node_modules/react-dev-utils
npm error       react-dev-utils@"^12.0.1" from react-scripts@5.0.1
npm error       node_modules/react-scripts
npm error         react-scripts@"5.0.1" from the root project
npm error   1 more (tsutils)
npm error
npm error Could not resolve dependency:
npm error peerOptional typescript@"^3.2.1 || ^4" from react-scripts@5.0.1
npm error node_modules/react-scripts
npm error   react-scripts@"5.0.1" from the root project

Add the following to the package.json to solve the problem: 1

+ "overrides": {
+   "typescript": "^5.5.4"
+ },

[!IMPORTANT] The version of overrides must be the same as the version of TypeScript in dependencies.

Migrate from Create React App to NextJS

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start"
},
$ npm uninstall react-scripts
$ npm uninstall react-router-dom
$ npm install next

Dangerously Set innerHTML

https://dev.to/shareef/rendering-markdown-made-easy-with-react-markdown-in-reactjs-and-nextjs-web-apps-259d

In HTML, <div> cannot be a descendant of <p>.
This will cause a hydration error.
...
  <Markdown>
    <p>
    ^^^
      <http://localhost:3000/_next/static/chunks/src_dd03ef._.js:150:225>
        <div>
+ const isImageNode = (node: any): node is Element => {
+   return node && node.type === 'element' && node.tagName === 'img';
+ };
 
const MarkdownRenderer: React.FC<MarkdownRendererProps> = ({ content }) => (
  <ReactMarkdown
    remarkPlugins={[remarkGfm]}
    rehypePlugins={[rehypeRaw]}
    components={{
+       p: ({ node, children }) => {
+         const hasImage = node && node.children && node.children.some(isImageNode);
+         if (hasImage) {
+           return <>{children}</>;
+         }
+         return <p>{children}</p>;
+       },
    ...
  >
    {content}
  </ReactMarkdown>
);
 
export default MarkdownRenderer;

React Wrap Balancer

React Wrap Balancer is a simple React Component that makes your titles more readable in different viewport sizes. It improves the wrapping to avoid situations like single word in the last line, makes the content more “balanced”: 1

Simple React Component That Makes Titles More Readable

Usage

We have to install the package first:

npm i react-wrap-balancer

Then we can use it in our project:

import Balancer from 'react-wrap-balancer'
 
// ...
 
function Title() {
  return (
    <h1>
      <Balancer>My Awesome Title</Balancer>
    </h1>
  )
}

If we have multiple <Balancer> components used, we can wrap them with <Provider> to share the re-balance logic:

import { Provider } from 'react-wrap-balancer'
 
// ...
 
function App() {
  return (
    <Provider>
      <MyApp/>
    </Provider>
  )
}

Next.js Image Fast Loading

SEO Tools

Open Graph Meta Tags

Loose Autocomplete

acceptString = (str: "foo" | "bar" | (string & {})) => {
  console.log(str);
};
 
acceptString("baz");

https://www.threads.net/@jimmy.chiang/post/C_vl632ygU_/?xmt=AQGzwdnxgbmCCfAF8xCgI2zZiemQtJDR7omD6Mb26Ge3CA

https://www.youtube.com/live/8HoOxOd86M4?t=2778&si=P2mxsBXVq8UmrAX_

Robots.txt

/**
 * https://www.cloudflare.com/zh-tw/learning/bots/what-is-robots-txt/
 * https://www.cloudflare.com/robots.txt
 * https://github.com/vercel/vercel/blob/3e4223684609dbdb7d9a2b286294fe07941bf0d4/examples/hydrogen-2/app/routes/%5Brobots.txt%5D.tsx#
 * https://github.com/vercel/vercel/blob/3e4223684609dbdb7d9a2b286294fe07941bf0d4/packages/cli/test/dev/integration-2.test.ts#
 * https://github.com/vercel/vercel/blob/3e4223684609dbdb7d9a2b286294fe07941bf0d4/examples/hydrogen/src/routes/robots.txt.server.ts
 * @returns 
 */
 
const robotsTxtContent = `
#               ________
#    __,_,     |        |
#   [_|_/      |   OK   |
#    //        |________|
#  _//    __  /
# (_|)   |@@|
#  \\ \\__ \\--/ __
#   \\o__|----|  |   __
#       \\ }{ /\\ )_ / _\\
#       /\\__\\/ \\__O (__
#      (--/\\--)    \\__/
#      _)(  )(_
#     \`---''---\`
User-agent: *
Disallow:
`;
/**
 * This API endpoint generates a robots.txt file. Use this to control
 * access to your resources from SEO crawlers.
 * Learn more: https://developers.google.com/search/docs/advanced/robots/create-robots-txt
 */
 
import type {HydrogenRequest} from '@shopify/hydrogen';
 
export async function api(request) {
  const url = new URL(request.url);
 
  return new Response(robotsTxtData({url: url.origin}), {
    headers: {
      'content-type': 'text/plain',
      // Cache for 24 hours
      'cache-control': `max-age=${60 * 60 * 24}`,
    },
  });
}
 
function robotsTxtData({url}: {url: string}) {
  const sitemapUrl = url ? `${url}/sitemap.xml` : undefined;
 
  return `
User-agent: *
Disallow: /admin
Disallow: /cart
Disallow: /orders
Disallow: /checkouts/
Disallow: /checkout
Disallow: /carts
Disallow: /account
${sitemapUrl ? `Sitemap: ${sitemapUrl}` : ''}
 
# Google adsbot ignores robots.txt unless specifically named!
User-agent: adsbot-google
Disallow: /checkouts/
Disallow: /checkout
Disallow: /carts
Disallow: /orders
 
User-agent: Pinterest
Crawl-delay: 1
`.trim();
}

Font Optimization

refactor(font): add Roboto font and update layout to optimize with next/font (#556)

Before

src/app/global.css
.root {
  /* font-family */
-  --ff-poppins: "Poppins", sans-serif;
}
 
- html {
-   font-family: var(--ff-poppins);
- }
src/app/font.ts
import { Roboto } from "next/font/google";
 
export const roboto = Roboto({
  weight: ['400', '700'],
  style: ['normal', 'italic'],
  subsets: ['latin'],
  display: 'swap',
})
src/app/layout.tsx
import { roboto } from "./font";
 
const RootLayout = (props: RootLayoutProps) => {
  const { children } = props
  return (
-    <html lang="en">
+    <html lang="en" className={`${roboto.className}`}>

Dynamic Manifest

Next.js provides a convenient way to generate a web app manifest dynamically using its metadata API. This approach allows you to customize your manifest based on your application’s configuration or environment. This allows browsers to present the web app similarly to native applications, enabling features like installation on the home screen and full-screen display. 2

RSS Feed

Minimize main-thread work

How to minimize the “Minimize main-thread work” in nextjs? #19436

How to minimize main thread work in React Component https://aatifbandey.medium.com/reduce-main-thread-work-in-react-component-a90c9bc1d9b3

Web Vitals

https://github.com/luciancah/nextjs-ko/blob/7f67677d32f7247d0d468f1b3e1bceb43a6e03bd/pages/docs/app/building-your-application/optimizing/analytics.mdx#L17 https://github.com/axiomhq/next-axiom/tree/0ad6cf706dc154b17bd65e11d850a8c2b710db61/src/webVitals

Using setTimeout with Promise for Delayed Execution

When testing rendering effects or simulating delays in asynchronous operations, you can use setTimeout wrapped in a Promise. This approach ensures clean and readable code, especially in async/await contexts.

Code Example

To add a timeout in your tests or development workflows:

await new Promise(resolve => setTimeout(resolve, 3000));

Explanation

  1. setTimeout: Executes a function after a specified delay (in milliseconds).
  2. Promise: Allows setTimeout to work seamlessly with async/await.
  3. 3000: Represents the delay in milliseconds (3 seconds in this example).

When to Use

  • To simulate network latency or slow computations.
  • To test skeleton loaders or other rendering effects.
  • To delay execution for debugging or animations.

Usage in a Function

async function simulateLoadingEffect() {
  console.log('Loading...');
  await new Promise(resolve => setTimeout(resolve, 3000));
  console.log('Done!');
}
 
simulateLoadingEffect();

Output

  1. Displays “Loading…”
  2. Waits for 3 seconds
  3. Displays “Done!“

application/ld+json

Example - Apple

<script type="application/ld+json">
	{
		"@context": "http://schema.org",
		"@id": "https://www.apple.com/#organization",
		"@type": "Organization",
		"name": "Apple",
		"url": "https://www.apple.com/",
		"logo": "https://www.apple.com/ac/structured-data/images/knowledge_graph_logo.png?202110180743",
		"subOrganization": {
			"@type": "Organization",
			"name": "Apple Support",
			"url": "https://support.apple.com",
	    "@id": "https://support.apple.com/#organization"
		},
		"contactPoint": [
			{
				"@type": "ContactPoint",
				"telephone": "+1-800-692-7753",
				"contactType": "sales",
				"areaServed": "US"
			},
			{
				"@type": "ContactPoint",
				"telephone": "+1-800-275-2273",
				"contactType": "technical support",
				"areaServed": "US",
				"availableLanguage": ["EN", "ES"]
			},
			{
				"@type": "ContactPoint",
				"telephone": "+1-800-275-2273",
				"contactType": "customer support",
				"areaServed": "US",
				"availableLanguage": ["EN", "ES"]
			}
		],
		"sameAs": [
			"http://www.wikidata.org/entity/Q312",
			"https://www.youtube.com/user/Apple",
			"https://www.linkedin.com/company/apple",
			"https://www.facebook.com/Apple",
			"https://www.twitter.com/Apple"
		]
	}
</script>

Example - honghong.me

https://github.com/tszhong0411/honghong.me/blob/main/apps/web/src/app/page.tsx

$ npm i schema-dts
const jsonLd: WithContext<WebSite> = {
  '@context': 'https://schema.org',
  '@type': 'WebSite',
  name: SITE_TITLE,
  description: SITE_DESCRIPTION,
  url: SITE_URL,
  author: {
    '@type': 'Person',
    name: SITE_NAME,
    url: SITE_URL,
    sameAs: [SITE_FACEBOOK_URL, SITE_INSTAGRAM_URL, SITE_X_URL, SITE_GITHUB_URL, SITE_YOUTUBE_URL]
  },
  mainEntityOfPage: {
    '@type': 'WebPage',
    '@id': SITE_URL
  },
  inLanguage: 'en-US',
  copyrightYear: new Date().getFullYear(),
  keywords: SITE_KEYWORDS,
  dateCreated: '2020-12-05',
  dateModified: new Date().toISOString()
}
 
<script
  type='application/ld+json'
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>

References

Footnotes

  1. (react-scripts) Support for TypeScript 5.x #13080 2

  2. Generate a Web App Manifest with Next.js