avatarIevgenii Spitsyn

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

14791

Abstract

ljs-attr">id</span>: <span class="hljs-built_in">number</span>; <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>; <span class="hljs-attr">content</span>: <span class="hljs-built_in">string</span>; <span class="hljs-attr">date</span>: <span class="hljs-built_in">string</span>; }</pre></div><p id="7ee0">Now, create a function to fetch some example data. For the sake of this example, let’s create a file called <code>data.ts</code>:</p><div id="517e"><pre><span class="hljs-comment">// lib/data.ts</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">Post</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'../types/Post'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getAllPosts</span>(<span class="hljs-params"></span>): <span class="hljs-title class_">Post</span>[] { <span class="hljs-comment">// Fetch posts from an API or mock data</span> <span class="hljs-keyword">return</span> [ { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">title</span>: <span class="hljs-string">'First Post'</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">'Content of the first post...'</span>, <span class="hljs-attr">date</span>: <span class="hljs-string">'2023-11-23'</span>, }, <span class="hljs-comment">// Add more posts here if needed</span> ]; }</pre></div><p id="325a">Create a page to display these posts. For instance, a simple page that lists all posts:</p><div id="b27d"><pre><span class="hljs-comment">// pages/posts.tsx</span> <span class="hljs-keyword">import</span> { <span class="hljs-title class_">GetStaticProps</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>; <span class="hljs-keyword">import</span> { <span class="hljs-title class_">Post</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'../types/Post'</span>; <span class="hljs-keyword">import</span> { getAllPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">'../lib/data'</span>;

<span class="hljs-keyword">interface</span> <span class="hljs-title class_">PostsProps</span> { <span class="hljs-attr">posts</span>: <span class="hljs-title class_">Post</span>[]; }

<span class="hljs-keyword">const</span> <span class="hljs-title function_">Posts</span> = (<span class="hljs-params">{ posts }: PostsProps</span>) => { <span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Blog Posts<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"><<span class="hljs-name">ul</span>></span> {posts.map((post) => ( <span class="hljs-tag"><<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{post.id}</span>></span> <span class="hljs-tag"><<span class="hljs-name">h2</span>></span>{post.title}<span class="hljs-tag"></<span class="hljs-name">h2</span>></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>{post.content}<span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Date: {post.date}<span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"></<span class="hljs-name">li</span>></span> ))} <span class="hljs-tag"></<span class="hljs-name">ul</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ); };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-attr">getStaticProps</span>: <span class="hljs-title class_">GetStaticProps</span> = <span class="hljs-keyword">async</span> () => { <span class="hljs-keyword">const</span> posts = <span class="hljs-title function_">getAllPosts</span>(); <span class="hljs-keyword">return</span> { <span class="hljs-attr">props</span>: { posts }, }; };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">Posts</span>;</pre></div><p id="8eef">Run your Next.js app using:</p><div id="a08f"><pre>npm run dev</pre></div><p id="4098">Then, access the posts page at <code>http://localhost:3000/posts</code> in your browser. This example fetches data during the build time using <code>getStaticProps</code> and displays it in a simple list. You can expand upon this structure by adding more pages, components, and styling as needed for your static site.</p><h2 id="eb28">Middleware</h2><p id="af50">Middleware is a new addition to NextJS that offers additional control over your app’s server-side rendering process. It enables you to modify incoming requests, perform authentication, make API calls, or handle redirects before the page is rendered. This allows you to keep complex logic separate from your page’s React components and create cleaner components and functions.</p><p id="b418"><b>Example:</b></p><div id="126c"><pre><span class="hljs-comment">// pages/_middleware.ts</span> <span class="hljs-keyword">import</span> {<span class="hljs-title class_">NextRequest</span>, <span class="hljs-title class_">NextResponse</span>} <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">middleware</span>(<span class="hljs-params">req: NextRequest</span>) { <span class="hljs-keyword">const</span> userIsAuthenticated = <span class="hljs-title function_">checkAuthentication</span>(req);

<span class="hljs-keyword">if</span> (!userIsAuthenticated) { <span class="hljs-keyword">return</span> <span class="hljs-title class_">NextResponse</span>.<span class="hljs-title function_">redirect</span>(<span class="hljs-string">'/login'</span>); }

<span class="hljs-keyword">return</span> <span class="hljs-title class_">NextResponse</span>.<span class="hljs-title function_">next</span>(); }</pre></div><p id="4996">In the example above, the middleware checks if the user is authenticated before rendering any page. If the user is not authenticated, they are redirected to the login page.</p><h2 id="bcff">Custom Server</h2><p id="08e1">Some applications with specific needs may require a custom server to handle unique setups like long-lasting connections or WebSockets. NextJS provides a built-in server but also allows you to create a custom server using Express or other popular libraries.</p><p id="6848"><b>Example:</b></p><div id="9198"><pre><span class="hljs-comment">// server.ts</span> <span class="hljs-keyword">import</span> next <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>; <span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;

<span class="hljs-keyword">const</span> dev = process.<span class="hljs-property">env</span>.<span class="hljs-property">NODE_ENV</span> !== <span class="hljs-string">'production'</span>; <span class="hljs-keyword">const</span> nextApp = <span class="hljs-title function_">next</span>({dev}); <span class="hljs-keyword">const</span> handle = nextApp.<span class="hljs-title function_">getRequestHandler</span>();

nextApp.<span class="hljs-title function_">prepare</span>().<span class="hljs-title function_">then</span>(<span class="hljs-function">() =></span> { <span class="hljs-keyword">const</span> app = <span class="hljs-title function_">express</span>();

app.<span class="hljs-title function_">get</span>(<span class="hljs-string">'/custom-route'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =></span> { res.<span class="hljs-title function_">send</span>(<span class="hljs-string">'This is your custom route using Express'</span>); });

app.<span class="hljs-title function_">all</span>(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =></span> { <span class="hljs-keyword">return</span> <span class="hljs-title function_">handle</span>(req, res); });

app.<span class="hljs-title function_">listen</span>(<span class="hljs-number">3000</span>, <span class="hljs-function">() =></span> { <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'> Ready on http://localhost:3000'</span>); }); });</pre></div><p id="cbf5">In the example above, a custom server using Express listens on port 3000 and serves a custom route page. All other requests are handled by the NextJS app.</p><h2 id="802a">API routes</h2><p id="965a">Next.js includes built-in support to create API routes in your application. To create an API route, you need to create a new file inside the <code>pages/api</code> folder. The file name will determine the endpoint (e.g., <code>pages/api/user.ts</code> will create a <code>/api/user</code> endpoint).</p><p id="6112"><b>Example:</b></p><div id="fa45"><pre><span class="hljs-comment">// pages/api/example.ts</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title class_">NextApiRequest</span>, <span class="hljs-title class_">NextApiResponse</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;

<span class="hljs-comment">// Define types for request and response</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">CustomRequest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">NextApiRequest</span> { <span class="hljs-comment">// Define any custom properties you might need</span> <span class="hljs-comment">// For example, if you expect a certain parameter in the request</span> <span class="hljs-attr">query</span>: { <span class="hljs-attr">param</span>: <span class="hljs-built_in">string</span>; }; }

<span class="hljs-keyword">interface</span> <span class="hljs-title class_">CustomResponse</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">NextApiResponse</span> { <span class="hljs-comment">// Define any custom properties for the response</span> }

<span class="hljs-comment">// Example API route handler</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params">req: CustomRequest, res: CustomResponse</span>) { <span class="hljs-keyword">const</span> { <span class="hljs-attr">query</span>: { param }, } = req;

<span class="hljs-comment">// Access the query parameter</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Received param:'</span>, param);

<span class="hljs-keyword">try</span> { <span class="hljs-comment">// Perform any necessary operations</span> <span class="hljs-comment">// Return data in JSON format</span> res.<span class="hljs-title function_">status</span>(<span class="hljs-number">200</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">message</span>: <span class="hljs-string">Received param: <span class="hljs-subst">${param}</span></span> }); } <span class="hljs-keyword">catch</span> (error) { <span class="hljs-comment">// Handle errors</span> res.<span class="hljs-title function_">status</span>(<span class="hljs-number">500</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Internal server error'</span> }); } }</pre></div><p id="6bf7">You can define custom types for the request and response objects to add type safety to your code.</p><p id="5e10">Remember to restart your Next.js server after creating or modifying API routes for the changes to take effect.</p><h2 id="f017">Hot Module Replacement (HMR)</h2><p id="2c3e">Next.js provides built-in HMR, which allows updates to be pushed to the browser without needing a page refresh. This enhances the developer experience by providing a faster feedback loop during development.</p><p id="2e8a"><b>Example:</b></p><p id="c436"><b>Install Required Packages:</b></p><div id="f180"><pre>npm install --save-dev @types/webpack-env</pre></div><p id="2a6e"><b>Configure <code>next.config.js</code>:</b></p><p id="7663">Inside your project’s root, create or update <code>next.config.js</code> to enable HMR:</p><div id="a3d0"><pre><span class="hljs-comment">// next.config.js</span>

<span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = { <span class="hljs-attr">webpack</span>: <span class="hljs-function">(<span class="hljs-params">config, { dev }</span>) =></span> { <span class="hljs-keyword">if</span> (dev) { config.<span class="hljs-property">plugins</span>.<span class="hljs-title function_">push</span>(<span class="hljs-keyword">new</span> webpack.<span class="hljs-title class_">HotModuleReplacementPlugin</span>()); } <span class="hljs-keyword">return</span> config; }, };</pre></div><p id="bea6"><b>Create a TypeScript Page:</b></p><p id="1618">For example, create a TypeScript page named <code>index.tsx</code> in the <code>pages</code> directory:</p><div id="9f64"><pre><span class="hljs-comment">// pages/index.tsx</span>

<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> <span class="hljs-title function_">HomePage</span> = (<span class="hljs-params"></span>) => { <span class="hljs-keyword">const</span> [count, setCount] = <span class="hljs-title function_">useState</span>(<span class="hljs-number">0</span>);

<span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Next.js with HMR + TypeScript<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>Count: {count}<span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hlj

Options

s-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =></span> setCount(count + 1)}>Increment<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ); };

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">HomePage</span>;</pre></div><p id="7d75"><b>Start the Development Server:</b></p><p id="b313">Run your Next.js application in development mode:</p><div id="9d8b"><pre>npm run dev</pre></div><p id="485b">This setup should allow you to modify the TypeScript files in the <code>pages</code> directory and witness the changes reflected in your running application without needing a full refresh.</p><p id="0969">Remember, HMR might have limitations depending on the complexity of your code and the specific libraries or components you’re using. However, it should work seamlessly for most common scenarios, providing live updates during development.</p><h2 id="8494">Automatic code splitting</h2><p id="66ab">Next.js automatically splits the code into smaller JavaScript bundles, so that users only download the necessary code for each page. This results in faster page loading times and better performance.</p><h2 id="dece">File-system based routing</h2><p id="3fc4">Next.js uses file-system-based routing for easy routing setup. Names of the files in the “pages” folder automatically become routes, making it simple to create and manage routes in your application without explicit router configuration.</p><h2 id="7133">Customized environments and configurations</h2><p id="5792">Next.js offers customizable environments and configurations, allowing you to customize various settings like the base path, assets directory, and target environments.</p><h2 id="3363">Wide range of plugins</h2><p id="015e">Next.js has a large ecosystem of plugins that simplify integrations with various tools and platforms like CSS-In-JS libraries, Sass, Google Analytics, and more. You can use these plugins to create feature-rich applications with minimal effort.</p><h2 id="ab30">BONUS. NextJS + ChatGPT API code sample</h2><p id="f390">Let’s have some fun time now and build an example that sets up a simple web app with a form where users can input text, send it to the server, and receive a response generated by ChatGPT.</p><p id="3b63"><b>Create a new Next.js app with TypeScript:</b></p><div id="318f"><pre>npx create-next-app my-chat-app --typescript <span class="hljs-built_in">cd</span> my-chat-app</pre></div><p id="0fe4"><b>Install required dependencies:</b></p><div id="5889"><pre>npm install openai axios</pre></div><p id="ce7d"><b>Create a new TypeScript file (<code>src/pages/index.tsx</code>) for the main page:</b></p><div id="f295"><pre><span class="hljs-comment">// src/pages/index.tsx</span> <span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Home</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">const</span> [inputText, setInputText] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">''</span>); <span class="hljs-keyword">const</span> [response, setResponse] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">''</span>);

<span class="hljs-keyword">const</span> <span class="hljs-title function_">handleInputChange</span> = (<span class="hljs-params">e: React.ChangeEvent<HTMLTextAreaElement></span>) => { <span class="hljs-title function_">setInputText</span>(e.<span class="hljs-property">target</span>.<span class="hljs-property">value</span>); };

<span class="hljs-keyword">const</span> <span class="hljs-title function_">handleSubmit</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">const</span> apiUrl = <span class="hljs-string">'/api/chat'</span>; <span class="hljs-comment">// Endpoint to handle GPT chat logic</span>

  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">post</span>(apiUrl, { <span class="hljs-attr">text</span>: inputText });

  <span class="hljs-title function_">setResponse</span>(res.<span class="hljs-property">data</span>.<span class="hljs-property">message</span>);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Error:'</span>, error);
}

};

<span class="hljs-keyword">return</span> ( <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>Chat with GPT-3<span class="hljs-tag"></<span class="hljs-name">h1</span>></span> <span class="hljs-tag"><<span class="hljs-name">textarea</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your message..."</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{inputText}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleInputChange}</span> /></span> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSubmit}</span>></span>Send<span class="hljs-tag"></<span class="hljs-name">button</span>></span> <span class="hljs-tag"><<span class="hljs-name">div</span>></span> <span class="hljs-tag"><<span class="hljs-name">h2</span>></span>Response:<span class="hljs-tag"></<span class="hljs-name">h2</span>></span> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>{response}<span class="hljs-tag"></<span class="hljs-name">p</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span> ); }</pre></div><p id="8a15"><b>Create an API route to handle the ChatGPT logic (<code>src/pages/api/chat.ts</code>):</b></p><div id="5957"><pre><span class="hljs-comment">// src/pages/api/chat.ts</span> <span class="hljs-keyword">import</span> type { <span class="hljs-title class_">NextApiRequest</span>, <span class="hljs-title class_">NextApiResponse</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>; <span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">handler</span>(<span class="hljs-params">req: NextApiRequest, res: NextApiResponse</span>) { <span class="hljs-keyword">if</span> (req.<span class="hljs-property">method</span> === <span class="hljs-string">'POST'</span>) { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">const</span> { text } = req.<span class="hljs-property">body</span>; <span class="hljs-keyword">const</span> openaiApiKey = <span class="hljs-string">'YOUR_OPENAI_API_KEY'</span>; <span class="hljs-comment">// Replace with your OpenAI API key</span>

  <span class="hljs-keyword">const</span> gptResponse = <span class="hljs-keyword">await</span> axios.<span class="hljs-title function_">post</span>(
    <span class="hljs-string">'https://api.openai.com/v1/engines/davinci/completions'</span>,
    {
      <span class="hljs-attr">prompt</span>: text,
      <span class="hljs-attr">max_tokens</span>: <span class="hljs-number">100</span>,
    },
    {
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        <span class="hljs-title class_">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${openaiApiKey}</span>`</span>,
      },
    }
  );

  <span class="hljs-keyword">const</span> message = gptResponse.<span class="hljs-property">data</span>.<span class="hljs-property">choices</span>[<span class="hljs-number">0</span>].<span class="hljs-property">text</span>.<span class="hljs-title function_">trim</span>();
  res.<span class="hljs-title function_">status</span>(<span class="hljs-number">200</span>).<span class="hljs-title function_">json</span>({ message });
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Error:'</span>, error);
  res.<span class="hljs-title function_">status</span>(<span class="hljs-number">500</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Server error'</span> });
}

} <span class="hljs-keyword">else</span> { res.<span class="hljs-title function_">status</span>(<span class="hljs-number">405</span>).<span class="hljs-title function_">json</span>({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Method not allowed'</span> }); } }</pre></div><p id="cc7e">Replace <code>'YOUR_OPENAI_API_KEY'</code> with your actual OpenAI API key.</p><p id="43df"><b>Start the development server:</b></p><div id="1ce7"><pre>npm run dev</pre></div><p id="616c">This basic setup allows users to enter text, and submit it to the server, which then uses the OpenAI GPT-3 API to generate a response. Make sure to handle errors and edge cases as needed, and always secure your API keys properly, especially in production environments.</p><h2 id="2494">Conclusion</h2><p id="2a45">The advanced features of NextJS, when combined with React and TypeScript, allow you to build highly optimized and performant web applications. By adopting dynamic imports, Incremental Static Regeneration, Server-side rendering, Static site generation, Automatic static optimization, middleware, custom servers, HMR, and other features you can create versatile and scalable applications suitable for various production environments.</p><p id="38fe">That’s it! Give Next.js a try and see how it can streamline your React development process!</p><p id="18a9">You may also check out my other articles on Exciting React and Vue Features in 2023, ThreeJS/Shaders practicing and Entertaining Web Geometry:</p><p id="bd1d">You may also check out my other articles on Exciting React Features in 2023, ThreeJS/Shaders practicing and Entertaining Web Geometry:</p><div id="bb06" class="link-block"> <a href="https://betterprogramming.pub/exciting-react-features-in-2023-a-look-into-the-future-of-web-development-440107a3129d"> <div> <div> <h2>Exciting React Features in 2023: A Look into the Future of Web Development</h2> <div><h3>undefined</h3></div> <div><p>undefined</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*9Eb6Fd_Oa5uSZNZfHaRBLg.jpeg)"></div> </div> </div> </a> </div><div id="be05" class="link-block"> <a href="https://betterprogramming.pub/exciting-vue-3-features-in-2023-a-look-into-the-future-of-web-development-part-2-148e644d7df3"> <div> <div> <h2>Exciting Vue 3 Features in 2023: A Look into the Future of Web Development (Part 2)</h2> <div><h3>In the first part of this overview, we looked at the React exciting new features in 2023</h3></div> <div><p>betterprogramming.pub</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*jBuqufyfckQk35w3FPBKkQ.jpeg)"></div> </div> </div> </a> </div><div id="8499" class="link-block"> <a href="https://readmedium.com/threejs-shaders-practicing-building-a-performant-interactive-cube-97bcbd62abc9"> <div> <div> <h2>ThreeJS/Shaders practicing: Building a performant interactive cube.</h2> <div><h3>What can be more ordinary than start practicing WebGL/ThreeJS with building a second (after sphere) common shape — …</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*0_vyZiSm1kg1OUHfk3-Ovw.png)"></div> </div> </div> </a> </div><div id="9e2f" class="link-block"> <a href="https://betterprogramming.pub/entertaining-web-geometry-building-an-interactive-3d-css-hexagon-a9b5f535d06e"> <div> <div> <h2>Entertaining Web Geometry: Building an Interactive 3D CSS Hexagon</h2> <div><h3>An introduction to the 3D Hexagon and its React wrapper</h3></div> <div><p>betterprogramming.pub</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*JOl1O87bmA_qoQbQMR6uag.png)"></div> </div> </div> </a> </div><p id="ebca">Find me on social networks: <a href="https://www.instagram.com/ievgenys/">Instagram</a>, <a href="https://www.facebook.com/profile.php?id=678418472">Facebook</a>, and <a href="https://twitter.com/IevgeniiSp">Twitter</a>.</p><p id="1045">Also, if you like my articles, you may support my further work:</p><figure id="c021"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*zQk1gyjNv5GK_hDqCfQwGQ.jpeg"><figcaption>Photo by <a href="https://unsplash.com/@nate_dumlao">Nathan Dumlao</a> on <a href="https://unsplash.com/photos/6VhPY27jdps">Unsplash</a></figcaption></figure><h2 id="0ce1">Enjoy and happy coding!</h2><p id="d150">Example (in TypeScript):</p></article></body>

NextJS: Leveraging advanced features with TypeScript

Best React frameworks: NextJS

NextJS Framework. Photo by Iconduck

NextJS, a popular React framework, has redefined the way developers build modern, lightning-fast web applications. It provides an excellent developer experience, out-of-the-box performance optimizations, and seamless integration with modern tools. The introduction of static site generation (SSG) and server-side rendering (SSR), along with incrementally static regeneration (ISR) in Next.js, has made it an almost indispensable tool in the ever-evolving world of web development.

Adding TypeScript, a strongly typed superset of JavaScript, to the mix can significantly enhance NextJS applications. TypeScript brings powerful features such as static typing, code navigation, and error reporting, which can save developers time and prevent countless bugs.

In this article, we will explore several advanced features of NextJS, illustrating their usage with TypeScript code examples. By the end of this guide, you will have a deeper understanding of how NextJS and TypeScript can combine to create truly exceptional web applications.

NextJS empowering an amazing React library. You may read about advanced React features in my other article:

Dynamic Imports

Dynamic imports are a modern JavaScript feature that enables developers to split the code into smaller bundles, permitting the loading of various parts (chunks) of your application on demand. This feature can significantly improve your app performance by reducing load time, especially for large and complex applications.

Example:

import dynamic from 'next/dynamic';

// Dynamically import the component
const DynamicComponent = dynamic(
  () => import('../components/DynamicComponent'),
  {
    ssr: false, // Disable server-side rendering for this component
  }
);

const PageComponent = () => (
  <div>
    <p>This is a static content</p>
    <DynamicComponent />
  </div>
);

export default PageComponent;

In the above example, the DynamicComponent will only be loaded when the PageComponent is rendered, thus significantly decreasing the initial loading time for your application.

Incremental Static Regeneration (ISR)

Incremental Static Regeneration is a feature that allows you to update static pages in the background after an initial render without requiring a full rebuild. This feature is especially useful for applications with frequently updated content but low server resources, as it offers optimal performance without slowing down the server.

Example:

export async function getStaticProps() {
  const data = await fetchAPI('https://api.example.com/data');

  return {
    props: {
      data,
    },
    revalidate: 60, // Regenerate the page every 60 seconds
  };
}

const PageComponent = ({data}) => (
  <div>
    <h1>Example Data</h1>
    {data.map((item) => (
      <div key={item.id}>{item.title}</div>
    ))}
  </div>
);

export default PageComponent;

In the example above, the revalidate the key specifies the regeneration interval. The page will be re-rendered only after 60 seconds have elapsed since the last successful request.

Automatic static optimization (ASO)

In Next.js, automatic static optimization is the default behavior for pages that are pre-rendered using getStaticProps or getStaticPaths. Combining this with TypeScript involves configuring Next.js to work seamlessly with TypeScript.

To benefit from this feature, all you need to do is avoid adding data-fetching methods like getInitialProps or getServerSideProps in your pages. Next.js will then generate static HTML at build time. Here's an example of a simple page written in Next.js.

Example:

// pages/example.tsx

import { GetStaticProps } from 'next';

interface ExampleProps {
  data: string;
}

const ExamplePage = ({ data }: ExampleProps) => {
  return (
    <div>
      <h1>Example Page</h1>
      <p>Data: {data}</p>
    </div>
  );
};

export const getStaticProps: GetStaticProps<ExampleProps> = async () => {
  // Fetch data (replace with your data fetching logic)
  const data = 'This is some dynamic data fetched at build time';

  return {
    props: {
      data,
    },
  };
};

export default ExamplePage;
  • The ExamplePage component represents your Next.js page.
  • getStaticProps is a function that Next.js calls at build time to fetch data for pre-rendering.
  • In this example, it fetches some data (could be from an API, database, etc.) and passes it as props to the ExamplePage component.
  • GetStaticProps is typed ExampleProps to define the structure of props that the page expects.

Remember, by using getStaticProps, Next.js will pre-render this page at build time and optimize it as a static page, serving the static HTML to the client.

Ensure that your tsconfig.json has proper TypeScript configuration for Next.js, and you can leverage TypeScript types and interfaces throughout your Next.js application for static optimization or any other functionalities.

Server-side rendering (SSR)

Server-side rendering (SSR) refers to rendering your web application on the server, and then sending the final HTML to the client. SSR can improve SEO and speed up the initial page load. Next.js makes it easy to achieve SSR by running your React components on the server.

To fetch data on the server side, we will use getServerSideProps

Example:

import { GetServerSideProps } from 'next';
import React from 'react';

interface HomeProps {
  serverTime: string;
}

const Home: React.FC<HomeProps> = ({ serverTime }) => {
  return (
    <div>
      <h1>Hello Next.js!</h1>
      <p>Server time: {serverTime}</p>
    </div>
  );
};

export const getServerSideProps: GetServerSideProps = async () => {
  // Fetch server time (this could be any asynchronous operation)
  const serverTime = new Date().toISOString();

  return {
    props: {
      serverTime,
    },
  };
};

export default Home;

In this example:

  • GetServerSideProps is used to fetch data at the request time before the page is rendered.
  • We define the Home component that receives serverTime as a prop fetched from the server.
  • Inside getServerSideProps, we retrieve the current server time and pass it as a prop to the Home component.

If you are interested in other interesting Frameworks rendering approaches you may read my article about advanced Vue features and its concept of createRenderer API:

Static site generation (SSG)

Next.js also lets you create fully static sites. You can achieve this by exporting your website during the build process.

To fetch data at build time, use the getStaticProps function.

Example:

As an example, let’s create a simple interface for a blog post:

// types/Post.ts
export interface Post {
  id: number;
  title: string;
  content: string;
  date: string;
}

Now, create a function to fetch some example data. For the sake of this example, let’s create a file called data.ts:

// lib/data.ts
import { Post } from '../types/Post';

export function getAllPosts(): Post[] {
  // Fetch posts from an API or mock data
  return [
    {
      id: 1,
      title: 'First Post',
      content: 'Content of the first post...',
      date: '2023-11-23',
    },
    // Add more posts here if needed
  ];
}

Create a page to display these posts. For instance, a simple page that lists all posts:

// pages/posts.tsx
import { GetStaticProps } from 'next';
import { Post } from '../types/Post';
import { getAllPosts } from '../lib/data';

interface PostsProps {
  posts: Post[];
}

const Posts = ({ posts }: PostsProps) => {
  return (
    <div>
      <h1>Blog Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <h2>{post.title}</h2>
            <p>{post.content}</p>
            <p>Date: {post.date}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export const getStaticProps: GetStaticProps = async () => {
  const posts = getAllPosts();
  return {
    props: { posts },
  };
};

export default Posts;

Run your Next.js app using:

npm run dev

Then, access the posts page at http://localhost:3000/posts in your browser. This example fetches data during the build time using getStaticProps and displays it in a simple list. You can expand upon this structure by adding more pages, components, and styling as needed for your static site.

Middleware

Middleware is a new addition to NextJS that offers additional control over your app’s server-side rendering process. It enables you to modify incoming requests, perform authentication, make API calls, or handle redirects before the page is rendered. This allows you to keep complex logic separate from your page’s React components and create cleaner components and functions.

Example:

// pages/_middleware.ts
import {NextRequest, NextResponse} from 'next/server';

export function middleware(req: NextRequest) {
  const userIsAuthenticated = checkAuthentication(req);

  if (!userIsAuthenticated) {
    return NextResponse.redirect('/login');
  }

  return NextResponse.next();
}

In the example above, the middleware checks if the user is authenticated before rendering any page. If the user is not authenticated, they are redirected to the login page.

Custom Server

Some applications with specific needs may require a custom server to handle unique setups like long-lasting connections or WebSockets. NextJS provides a built-in server but also allows you to create a custom server using Express or other popular libraries.

Example:

// server.ts
import next from 'next';
import express from 'express';

const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({dev});
const handle = nextApp.getRequestHandler();

nextApp.prepare().then(() => {
  const app = express();

  app.get('/custom-route', (req, res) => {
    res.send('This is your custom route using Express');
  });

  app.all('*', (req, res) => {
    return handle(req, res);
  });

  app.listen(3000, () => {
    console.log('> Ready on http://localhost:3000');
  });
});

In the example above, a custom server using Express listens on port 3000 and serves a custom route page. All other requests are handled by the NextJS app.

API routes

Next.js includes built-in support to create API routes in your application. To create an API route, you need to create a new file inside the pages/api folder. The file name will determine the endpoint (e.g., pages/api/user.ts will create a /api/user endpoint).

Example:

// pages/api/example.ts

import { NextApiRequest, NextApiResponse } from 'next';

// Define types for request and response
interface CustomRequest extends NextApiRequest {
  // Define any custom properties you might need
  // For example, if you expect a certain parameter in the request
  query: {
    param: string;
  };
}

interface CustomResponse extends NextApiResponse {
  // Define any custom properties for the response
}

// Example API route handler
export default function handler(req: CustomRequest, res: CustomResponse) {
  const {
    query: { param },
  } = req;

  // Access the query parameter
  console.log('Received param:', param);

  try {
    // Perform any necessary operations
    // Return data in JSON format
    res.status(200).json({ message: `Received param: ${param}` });
  } catch (error) {
    // Handle errors
    res.status(500).json({ error: 'Internal server error' });
  }
}

You can define custom types for the request and response objects to add type safety to your code.

Remember to restart your Next.js server after creating or modifying API routes for the changes to take effect.

Hot Module Replacement (HMR)

Next.js provides built-in HMR, which allows updates to be pushed to the browser without needing a page refresh. This enhances the developer experience by providing a faster feedback loop during development.

Example:

Install Required Packages:

npm install --save-dev @types/webpack-env

Configure next.config.js:

Inside your project’s root, create or update next.config.js to enable HMR:

// next.config.js

module.exports = {
  webpack: (config, { dev }) => {
    if (dev) {
      config.plugins.push(new webpack.HotModuleReplacementPlugin());
    }
    return config;
  },
};

Create a TypeScript Page:

For example, create a TypeScript page named index.tsx in the pages directory:

// pages/index.tsx

import { useState } from 'react';

const HomePage = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Next.js with HMR + TypeScript</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default HomePage;

Start the Development Server:

Run your Next.js application in development mode:

npm run dev

This setup should allow you to modify the TypeScript files in the pages directory and witness the changes reflected in your running application without needing a full refresh.

Remember, HMR might have limitations depending on the complexity of your code and the specific libraries or components you’re using. However, it should work seamlessly for most common scenarios, providing live updates during development.

Automatic code splitting

Next.js automatically splits the code into smaller JavaScript bundles, so that users only download the necessary code for each page. This results in faster page loading times and better performance.

File-system based routing

Next.js uses file-system-based routing for easy routing setup. Names of the files in the “pages” folder automatically become routes, making it simple to create and manage routes in your application without explicit router configuration.

Customized environments and configurations

Next.js offers customizable environments and configurations, allowing you to customize various settings like the base path, assets directory, and target environments.

Wide range of plugins

Next.js has a large ecosystem of plugins that simplify integrations with various tools and platforms like CSS-In-JS libraries, Sass, Google Analytics, and more. You can use these plugins to create feature-rich applications with minimal effort.

BONUS. NextJS + ChatGPT API code sample

Let’s have some fun time now and build an example that sets up a simple web app with a form where users can input text, send it to the server, and receive a response generated by ChatGPT.

Create a new Next.js app with TypeScript:

npx create-next-app my-chat-app --typescript
cd my-chat-app

Install required dependencies:

npm install openai axios

Create a new TypeScript file (src/pages/index.tsx) for the main page:

// src/pages/index.tsx
import { useState } from 'react';
import axios from 'axios';

export default function Home() {
  const [inputText, setInputText] = useState('');
  const [response, setResponse] = useState('');

  const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setInputText(e.target.value);
  };

  const handleSubmit = async () => {
    try {
      const apiUrl = '/api/chat'; // Endpoint to handle GPT chat logic

      const res = await axios.post(apiUrl, { text: inputText });

      setResponse(res.data.message);
    } catch (error) {
      console.error('Error:', error);
    }
  };

  return (
    <div>
      <h1>Chat with GPT-3</h1>
      <textarea
        placeholder="Enter your message..."
        value={inputText}
        onChange={handleInputChange}
      />
      <button onClick={handleSubmit}>Send</button>
      <div>
        <h2>Response:</h2>
        <p>{response}</p>
      </div>
    </div>
  );
}

Create an API route to handle the ChatGPT logic (src/pages/api/chat.ts):

// src/pages/api/chat.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import axios from 'axios';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    try {
      const { text } = req.body;
      const openaiApiKey = 'YOUR_OPENAI_API_KEY'; // Replace with your OpenAI API key

      const gptResponse = await axios.post(
        'https://api.openai.com/v1/engines/davinci/completions',
        {
          prompt: text,
          max_tokens: 100,
        },
        {
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${openaiApiKey}`,
          },
        }
      );

      const message = gptResponse.data.choices[0].text.trim();
      res.status(200).json({ message });
    } catch (error) {
      console.error('Error:', error);
      res.status(500).json({ message: 'Server error' });
    }
  } else {
    res.status(405).json({ message: 'Method not allowed' });
  }
}

Replace 'YOUR_OPENAI_API_KEY' with your actual OpenAI API key.

Start the development server:

npm run dev

This basic setup allows users to enter text, and submit it to the server, which then uses the OpenAI GPT-3 API to generate a response. Make sure to handle errors and edge cases as needed, and always secure your API keys properly, especially in production environments.

Conclusion

The advanced features of NextJS, when combined with React and TypeScript, allow you to build highly optimized and performant web applications. By adopting dynamic imports, Incremental Static Regeneration, Server-side rendering, Static site generation, Automatic static optimization, middleware, custom servers, HMR, and other features you can create versatile and scalable applications suitable for various production environments.

That’s it! Give Next.js a try and see how it can streamline your React development process!

You may also check out my other articles on Exciting React and Vue Features in 2023, ThreeJS/Shaders practicing and Entertaining Web Geometry:

You may also check out my other articles on Exciting React Features in 2023, ThreeJS/Shaders practicing and Entertaining Web Geometry:

Find me on social networks: Instagram, Facebook, and Twitter.

Also, if you like my articles, you may support my further work:

Photo by Nathan Dumlao on Unsplash

Enjoy and happy coding!

Example (in TypeScript):

Nextjs
Programming
React
Typescript
Framework
Recommended from ReadMedium