Skip to content

Optimization and Deployment

Once your Next.js app is ready, you need to make it fast and get it live on the internet. This guide will show you how to make your app run better (optimization) and how to put it online for everyone to use (deployment).

Optimization just means making your app faster and better for users. Let’s go through each area one by one.

Images are often the biggest files on a webpage, and if they are too large, they slow down your site. Next.js has a built-in tool called next/image that automatically makes images smaller and faster - you do not have to do anything extra.

import Image from 'next/image';
export default function MyComponent() {
return (
<Image
src="/path/to/image.jpg"
alt="Description of image"
width={500}
height={300}
quality={75} // Lower number = smaller file size, higher number = better quality
/>
);
}

If your images are coming from an external website (like a CMS or image hosting service), you need to tell Next.js which websites are allowed. You do this in your next.config.js file:

module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
port: '',
pathname: '/**',
},
],
}
};

Then you can use the Image component with that external image URL:

import Image from 'next/image';
export default function MyComponent() {
return (
<Image
src="https://example.com/path/to/image.jpg"
alt="Description of image"
width={500}
height={300}
quality={75}
/>
);
}

Sometimes you need to load scripts from other websites (like analytics tools or chat widgets). Loading these at the wrong time can slow down your page. Next.js has a built-in next/script component that helps you control when these scripts load.

Import this component in your layout or page file and use it like this:

import Script from 'next/script';
export default function MyComponent() {
return (
<>
{/* Load a script from an external URL */}
<Script
src="https://example.com/script.js"
strategy="lazyOnload" // Load this script after the page has fully loaded
/>
{/* You can also write inline JavaScript directly inside Script */}
<Script>
{`
console.log('Inline script executed');
console.log(window.location.href);
`}
</Script>
</>
);
}

The strategy prop controls when the script loads. Here are the options:

  • lazyOnload - loads after the whole page has finished loading. Best for things that are not urgent (like chat widgets).
  • afterInteractive - loads after the page loads and after the user starts interacting. Good for analytics.
  • beforeInteractive - loads before the page loads. Use this only for very important scripts that the page cannot work without.

Note: When writing inline scripts inside the Script component, wrap your code in backticks and curly braces like shown above. This is needed because it is a JavaScript expression inside JSX.

Fonts can also slow down your site if not handled properly. Next.js has a built-in tool called next/font that loads and optimizes fonts for you automatically.

You can use Google Fonts by importing them from next/font/google. You can also use your own custom font files by importing from next/font/local.

// app/layout.tsx
import { Inter, Roboto } from 'next/font/google';
import localFont from 'next/font/local';
const inter = Inter({
subsets: ['latin'],
});
const roboto = Roboto({
weight: '400',
subsets: ['latin'],
});
const myLocalFont = localFont({
src: '../public/my-font.woff2', // Your font file inside the public folder
variable: '--font-my-local', // A CSS variable name you can use later (optional)
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
// Apply fonts using their className or variable (do not use both at the same time - it causes an extra network request)
<html lang="en" className={`${inter.className} ${roboto.className} ${myLocalFont.className}`}>
<body>{children}</body>
</html>
);
}

If you use the variable option (like --font-my-local), you can use it in your CSS or Tailwind to apply the font to specific elements. For example in CSS:

.my-local-font {
font-family: var(--font-my-local);
}

SEO stands for Search Engine Optimization. It helps your website show up in Google search results. In Next.js, you add a metadata object to your layout or page file. This tells search engines things like your page title and description.

Use this when your metadata (title, description, etc.) is always the same and does not change:

// app/layout.tsx
export const metadata = {
title: 'My Next.js App',
description: 'This is my Next.js app built with the latest features.',
keywords: ['Next.js', 'React', 'JavaScript', 'Web Development'],
openGraph: {
title: 'My Next.js App',
description: 'This is my Next.js app built with the latest features.',
url: 'https://my-nextjs-app.com',
siteName: 'My Next.js App',
images: [
{
url: 'https://my-nextjs-app.com/og-image.jpg',
width: 800,
height: 600,
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'My Next.js App',
description: 'This is my Next.js app built with the latest features.',
images: ['https://my-nextjs-app.com/twitter-image.jpg'],
},
robots: {
index: true,
follow: true,
nocache: true,
},
icons: {
icon: '/favicon.ico',
shortcut: '/shortcut-icon.png',
apple: '/apple-icon.png',
},
manifest: '/site.webmanifest',
};

Use this when your metadata needs to change depending on the page content - for example, a blog post page where each post has a different title. This function runs on the server and fetches the data before sending the page to the user:

// app/page.tsx
import { Metadata } from 'next';
export async function generateMetadata(): Promise<Metadata> {
const data = await fetch('https://api.example.com/metadata').then(res => res.json());
return {
title: data.title,
description: data.description,
keywords: data.keywords,
openGraph: {
title: data.ogTitle,
description: data.ogDescription,
url: data.ogUrl,
siteName: data.ogSiteName,
images: [
{
url: data.ogImageUrl,
width: data.ogImageWidth,
height: data.ogImageHeight,
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: data.twitterTitle,
description: data.twitterDescription,
images: [data.twitterImageUrl],
},
robots: {
index: true,
follow: true,
nocache: true,
},
icons: {
icon: '/favicon.ico',
shortcut: '/shortcut-icon.png',
apple: '/apple-icon.png',
},
manifest: '/site.webmanifest',
};
}

Lazy loading means you only load a component when the user actually needs it, instead of loading everything at the start. This makes your app feel faster on the first load.

In Next.js, you can use the dynamic function to do this. However, only use lazy loading when really needed. It creates 2 network requests, and the total size of those requests is actually bigger than loading the component normally. So use it only for very large components that are not needed right away.

"use client";
import { useState } from 'react';
import dynamic from 'next/dynamic';
// This component will only be loaded when needed
const MyComponent = dynamic(() => import('@/components/MyComponent'), {
loading: () => <p>Loading...</p>, // Show this while the component is loading (optional)
ssr: false, // Do not render this component on the server (optional)
});
export default function Page() {
const [showComponent, setShowComponent] = useState(false);
return (
<div>
<h1>My Page</h1>
<Button onClick={() => setShowComponent(true)}>Load Component</Button>
{showComponent && <MyComponent />}
</div>
);
}

You can also lazy load big third-party libraries the same way. The example below shows how to load the lodash library only when the user clicks a button, instead of loading it when the page first opens.

Lodash is a popular JavaScript helper library with many useful functions for working with arrays, objects, and more. You can learn more at https://lodash.com/

"use client";
import { useState } from 'react';
export default function Page() {
const [showComponent, setShowComponent] = useState(false);
const data = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Alex' },
{ id: 3, name: 'Bob' },
{ id: 4, name: 'Drake' }
];
const handleClick = async () => {
setShowComponent(true);
const _ = (await import('lodash')).default; // Load lodash only when the button is clicked
const shuffledData = _.shuffle(data); // Shuffle the array randomly
console.log(shuffledData);
};
return (
<div>
<h1>My Page</h1>
<Button onClick={handleClick}>Load Component</Button>
{showComponent && <p>Check the console for shuffled data.</p>}
</div>
);
}

Deployment means putting your app on the internet so others can use it. Next.js makes this very easy, especially with a platform called Vercel - the same company that makes Next.js. You can also use other platforms like Netlify, AWS, or Cloudflare Pages/Workers.

Here are the steps to deploy your Next.js app to Vercel:

  1. Create a GitHub repository: If you have not done this already, create a new GitHub repository and push your Next.js project code to it.

  2. Sign up for Vercel: Go to Vercel and create a free account.

  3. Import your project: After logging in, click “New Project” and connect your GitHub repository. Vercel will automatically detect that it is a Next.js project.

  4. Configure build settings: Vercel will automatically fill in the right build settings for Next.js. You usually do not need to change anything. Just make sure the framework is set to “Next.js”.

    If you are using Prisma (a database tool), change the build command to: npx prisma generate && next build

  5. Set environment variables: If your app uses secret keys or API tokens (like a database URL or payment key), add them in the Vercel dashboard under the “Environment Variables” section. Never put these directly in your code.

  6. Deploy your app: Click “Deploy”. Vercel will build your app and put it online. When it is done, you will get a public URL where your app is live.