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 independencies
.
- Goodbye create-react-app
- Why is create-react-app still used by many? Very important #12628
- I’m trying to deploy my react app with vercel. I did every step in my terminal but at the end get error “Error: Command “npm install” exited with 1”
- Start a New React Project
- How can I get a compatible React package ecosystem having both react-scripts@x and typescript@^5?
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
- Migrating from Create React App to NextJS: A Practical Guide
- Migrating from create-react-app to Vite: A Quick and Easy Guide
- Migrating from Create React App
- Replace Create React App recommendation with Vite #5487
- CreativeTechGuy/ReactTemplate
- How to Migrate from create-react-app to Vite using Jest and Browserslist
- Migrating from Create React App
Dangerously Set innerHTML
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
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
- Build an Image Compressor with Next.js
- Next JS Image Optimization: Best Practices for Faster Loading
- 14 Best Practices For Next JS Speed Optimization
SEO Tools
Open Graph Meta Tags
Loose Autocomplete
acceptString = (str: "foo" | "bar" | (string & {})) => {
console.log(str);
};
acceptString("baz");
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
.root {
/* font-family */
- --ff-poppins: "Poppins", sans-serif;
}
- html {
- font-family: var(--ff-poppins);
- }
import { Roboto } from "next/font/google";
export const roboto = Roboto({
weight: ['400', '700'],
style: ['normal', 'italic'],
subsets: ['latin'],
display: 'swap',
})
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
setTimeout
: Executes a function after a specified delay (in milliseconds).Promise
: AllowssetTimeout
to work seamlessly withasync/await
.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
- Displays “Loading…”
- Waits for 3 seconds
- 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
- https://developers.google.com/search/docs/appearance/structured-data
- https://search.google.com/test/rich-results
- https://validator.schema.org/
- http://linter.structured-data.org/
- https://stackoverflow.com/questions/59318263/how-can-i-use-application-ldjson-in-nextjs
- https://z3388638.medium.com/%E4%BD%BF%E7%94%A8-json-ld-%E8%99%95%E7%90%86-seo-%E4%B8%A6%E8%AE%93-google-%E9%87%9D%E5%B0%8D%E4%B8%8D%E5%90%8C%E5%BD%A2%E5%BC%8F%E7%B6%B2%E7%AB%99%E5%81%9A%E7%8D%A8%E7%89%B9%E7%9A%84%E6%90%9C%E5%B0%8B%E7%B5%90%E6%9E%9C%E5%91%88%E7%8F%BE-9c74783c017a
- https://www.fabian-kleiser.de/blog/adding-json-ld-to-a-personal-website/
- https://jsonld.com/person/
- https://schemantra.com/schema_list/Person
- https://www.npmjs.com/package/schema-dts
- https://nextjs.org/learn-pages-router/seo/rendering-and-ranking/metadata