DEV Community

Tianya School
Tianya School

Posted on

Next.js and SSR: Building high-performance server-rendered applications

1. Create a project

Create a new Next.js project with the create-next-app scaffold:

npx create-next-app my-app
cd my-app
Enter fullscreen mode Exit fullscreen mode

2. Automatic SSR

In Next.js, each component of a .js or .jsx file will be automatically processed as an SSR page. For example, create a pages/index.js file:

// pages/index.js
import React from 'react';

function Home() {
  return (
    <div>
      <h1>Welcome to Next.js with SSR!</h1>
      <p>This is rendered on the server.</p>
    </div>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Run npm run dev to start the development server, visit http://localhost:3000, and you will find that the HTML already contains the server-rendered content.

3. Dynamic routing and data acquisition

Next.js supports dynamic routing, such as pages/posts/[id].js. Get data in getStaticPaths and getStaticProps or getServerSideProps:

// pages/posts/[id].js
import { useRouter } from 'next/router';
import { getPostById } from '../lib/api'; // Custom API to obtain data

export async function getServerSideProps(context) {
  const id = context.params.id;
  const post = await getPostById(id);

  return {
    props: {
      post,
    },
  };
}

function Post({ post }) {
  const router = useRouter();
  if (!router.isFallback && !post) {
    router.push('/404');
    return null;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

export default Post;
Enter fullscreen mode Exit fullscreen mode

4. Static optimization and pre-rendering

Next.js also supports static optimization and pre-rendering (Static Site Generation, SSG). Configure in getStaticPaths and getStaticProps:

// pages/posts/[id].js
export async function getStaticPaths() {
  // Get all possible dynamic paths
  const paths = await getPostIds();

  return {
    paths: paths.map((id) => `/posts/${id}`),
    fallback: false, // Or 'true' to return 404 for non-prerendered paths
  };
}

export async function getStaticProps(context) {
  const id = context.params.id;
  const post = await getPostById(id);

  return {
    props: {
      post,
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

6. Dynamic import and code splitting

Next.js supports dynamic import, which helps load code on demand and reduces initial loading time:

// pages/index.js
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/Dynamic'), {
  ssr: false, // Avoid rendering on the server
});

function Home() {
  return (
    <div>
      <h1>Welcome to Next.js with SSR!</h1>
      <DynamicComponent />
    </div>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

7. Optimize images and resources

Use next/image component to optimize image loading, automatic compression and resizing:

// pages/index.js
import Image from 'next/image';

function Home() {
  return (
    <div>
      <h1>Welcome to Next.js with SSR!</h1>
      <Image src="/example.jpg" alt="Example Image" width={500} height={300} />
    </div>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

8. Custom Servers

If you need more fine-grained control, you can create a custom server:

// server.js
const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');

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

app.prepare().then(() => {
  createServer((req, res) => {
    // Be sure to pass `true` as the second argument to `url.parse`.
    // This tells it to parse the query portion of the URL.
    const parsedUrl = parse(req.url, true);
    const { pathname } = parsedUrl;

    if (pathname === '/api') {
      // Custom API route handling
      // ...
    } else {
      handle(req, res, parsedUrl);
    }
  }).listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});
Enter fullscreen mode Exit fullscreen mode

9. Integrate third-party libraries and frameworks

Next.js allows you to easily integrate third-party libraries and frameworks, such as Redux, MobX, Apollo, etc.:

// pages/_app.js
import React from 'react';
import App from 'next/app';
import { Provider } from 'react-redux';
import store from '../store';

function MyApp({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;
Enter fullscreen mode Exit fullscreen mode

10. Optimize SEO

Next.js's SSR feature is SEO-friendly, but you can also optimize it through meta tags:

// pages/index.js
import Head from 'next/head';

function Home() {
  return (
    <>
      <Head>
        <title>My Next.js App</title>
        <meta name="description" content="This is an example of using Next.js with SEO." />
      </Head>
      <h1>Welcome to Next.js with SEO!</h1>
    </>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

11. Internationalization (i18n)

Next.js 10 introduces built-in i18n support, making it easy to implement multilingual websites:

// next.config.js
module.exports = {
  i18n: {
    locales: ['en', 'fr'],
    defaultLocale: 'en',
  },
};
Enter fullscreen mode Exit fullscreen mode

12. Serverless mode

Next.js supports Serverless mode, which is enabled by default on Vercel. In this mode, your application will run on demand, saving resource costs.

13. Web Workers

Use Web Workers in Next.js to handle intensive computing tasks to avoid blocking the main thread:

// components/Worker.js
import { useEffect } from 'react';
import { createWorker } from 'workerize-loader!./my-worker.js'; // Use workerize-loader to load worker files

function MyComponent() {
  const worker = createWorker();

  useEffect(() => {
    const result = worker.calculate(100000); // Calling worker methods
    result.then(console.log);
    return () => worker.terminate(); // Cleaning up workers
  }, []);

  return <div>Loading...</div>;
}

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

14. TypeScript Integration

Next.js supports TypeScript, adding type safety to your project:

Install typescript and @types/react.

Create a tsconfig.json configuration file.

Modify next.config.js to enable TypeScript support.

15. Custom Error Pages

Create pages/_error.js custom error pages:

// pages/_error.js
function Error({ statusCode }) {
  return (
    <div>
      <h1>Error {statusCode}</h1>
      <p>Something went wrong.</p>
    </div>
  );
}

Error.getInitialProps = ({ res, err }) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;

  return { statusCode };
};

export default Error;
Enter fullscreen mode Exit fullscreen mode

16. Deploy to Vercel

Next.js is perfectly integrated with Vercel. You can deploy it in just a few simple steps:

Create an account or log in on the Vercel website.

Authorize Vercel to access your GitHub or GitLab repository.

Select the project to deploy, and Vercel will automatically detect the Next.js configuration.

Set the project domain name and environment variables (if necessary).

Click the "Deploy" button, and Vercel will automatically build and deploy the application.

17. Performance monitoring and optimization

Use the built-in Lighthouse plugin of Next.js or third-party tools such as Google PageSpeed ​​Insights for performance evaluation. Optimize code, images, and other resources based on the report to improve loading speed and user experience.

Top comments (0)