A Next.js Starter with Styled Components
Start your setup by preparing your development environment.
Ensure you’re ready to execute commands within VSCode.
View > Terminal
or press Cmd + J
.Use VSCode’s terminal to keep all your work in one place.
Initialize a Next.js project with TypeScript and the App Router.
Set up the foundation for your Styled Components template.
cd ~/projects
).npx create-next-app@latest next-styled-template --typescript --eslint --app --src-dir --no-tailwind --no-experimental-app
next-styled-template
(or press Enter if pre-filled).src/
directory: Yes.The --src-dir
flag organizes your code neatly.
Move into your new project folder to begin setup.
Prepare to open and configure your project.
cd next-styled-template
Keep your terminal in the project root for all commands.
Ensure the code
command works in your VSCode terminal.
Open your project seamlessly within VSCode.
code .
fails with "command not found":
Cmd + Shift + P
(Mac) or Ctrl + Shift + P
(Windows/Linux).code --version
This step is needed only once per machine to enable the code
command.
Open your project in VSCode for editing.
Access your project files within the VSCode editor.
code .
next-styled-template
folder in your current VSCode window.Use code -r .
to reuse the current VSCode window if another is open.
Add Styled Components and other required packages.
Enable the features used in this template.
npm install styled-components @types/styled-components
npm install react-markdown remark-gfm react-syntax-highlighter
Check package.json
after installation to verify versions.
Configure Next.js to support Styled Components.
Ensure proper SSR and styling functionality.
next.config.ts
in VSCode and replace its contents with:
// next.config.ts /** @type {import('next').NextConfig} */ const nextConfig = { compiler: { styledComponents: true, }, }; export default nextConfig;
This enables Styled Components’ SSR capabilities.
Familiarize yourself with the project’s starting layout.
Know where to place files as you build the template.
After setup, your project structure looks like this:
next-styled-template/
├── node_modules/
├── public/
│ ├── favicon.ico
│ ├── next.svg
│ └── vercel.svg
├── src/
│ ├── app/
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── globals.css
│ │ └── favicon.ico
│ └── components/
├── .eslintrc.json
├── .gitignore
├── next-env.d.ts
├── next.config.ts
├── package.json
├── README.md
├── tsconfig.json
Use VSCode’s file explorer to visualize this structure.
Set up theme, styles, and SSR support for Styled Components.
Prepare reusable styling and ensure SSR compatibility.
src/lib/theme.ts
:
// src/lib/theme.ts export const theme = { colors: { primary: "#0070f3", textDark: "#ffffff", textLight: "#a0aec0", backgroundContent: "#2d3748", backgroundLight: "#4a5568", backgroundDark: "#1a202c", }, };
src/lib/styles.ts
:
// src/lib/styles.ts import styled from "styled-components"; import Image from "next/image"; export const PageContainer = styled.div` min-height: 100vh; display: flex; flex-direction: column; `; export const HeroContainer = styled.div` position: relative; height: 50vh; width: 100%; `; export const HeroImage = styled(Image)` object-fit: cover; width: 100%; height: 100%; `; export const HeroText = styled.div` position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: white; `; export const HeroTitle = styled.h1` font-size: 3rem; margin: 0; `; export const HeroSubtitle = styled.h2` font-size: 1.5rem; margin: 0; `; export const ContentContainer = styled.div` padding: 2rem; max-width: 1200px; margin: 0 auto; `; export const Container = styled.div` width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 1rem; `;
src/app/_document.tsx
:
// src/app/_document.tsx import Document, { Html, Head, Main, NextScript } from "next/document"; class MyDocument extends Document { render() { return ( <Html lang="en"> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
src/components/styled-components-registry.tsx
:
// src/components/styled-components-registry.tsx "use client"; import React, { useState } from "react"; import { useServerInsertedHTML } from "next/navigation"; import { ServerStyleSheet, StyleSheetManager } from "styled-components"; export default function StyledComponentsRegistry({ children, }: { children: React.ReactNode; }) { const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()); useServerInsertedHTML(() => { const styles = styledComponentsStyleSheet.getStyleElement(); styledComponentsStyleSheet.instance.clearTag(); return <>{styles}</>; }); if (typeof window !== "undefined") return <>{children}</>; return ( <StyleSheetManager sheet={styledComponentsStyleSheet.instance}> {children} </StyleSheetManager> ); }
src/components/ThemeWrapper.tsx
:
// src/components/ThemeWrapper.tsx "use client"; import { ThemeProvider } from "styled-components"; import { theme } from "../lib/theme"; // Adjust to src/lib/theme after setup import { ReactNode } from "react"; interface ThemeWrapperProps { children: ReactNode; } export default function ThemeWrapper({ children }: ThemeWrapperProps) { return <ThemeProvider theme={theme}>{children}</ThemeProvider>; }
Place reusable utilities in src/lib/
and client components in src/components/
.
Enhance the default homepage with a professional look using Styled Components.
Create an inviting entry point for your application.
src/app/page.tsx
with this polished version:
// src/app/page.tsx "use client"; import { useState } from "react"; import styled from "styled-components"; import { Container } from "../lib/styles"; // Adjust to src/lib/styles after setup import { theme } from "../lib/theme"; // Adjust to src/lib/theme after setup const WelcomeCard = styled.div` background: ${theme.colors.textDark}; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); padding: 2rem; max-width: 600px; margin: 2rem auto; transition: transform 0.3s ease; &:hover { transform: translateY(-5px); } `; const Title = styled.h1` font-size: 2.5rem; font-weight: 700; color: ${theme.colors.backgroundDark}; margin: 0 0 1rem; `; const Text = styled.p` font-size: 1.125rem; color: ${theme.colors.backgroundLight}; line-height: 1.6; margin: 0 0 1.5rem; `; const Button = styled.button` background: ${theme.colors.primary}; color: ${theme.colors.textDark}; border: none; padding: 0.75rem 1.5rem; font-size: 1rem; font-weight: 600; border-radius: 8px; cursor: pointer; transition: background 0.3s ease; &:hover { background: ${theme.colors.backgroundDark}; } `; const Progress = styled.div<{ active: boolean }>` height: 4px; background: ${theme.colors.textLight}; border-radius: 2px; overflow: hidden; margin-top: 1rem; &::after { content: ""; display: block; width: 50%; height: 100%; background: ${theme.colors.primary}; transform: translateX(${({ active }) => (active ? "100%" : "0")}); transition: transform 0.5s ease; } `; export default function Home() { const [isExploring, setIsExploring] = useState(false); return ( <Container> <WelcomeCard> <Title>Welcome to Your Next.js Journey</Title> <Text> This template provides a sleek foundation with Styled Components and the App Router. Click below to start exploring and building your app! </Text> <Button onClick={() => setIsExploring(!isExploring)}> {isExploring ? "Pause Exploration" : "Start Exploring"} </Button> <Progress active={isExploring} /> </WelcomeCard> </Container> ); }
Use Styled Components to maintain a consistent look across your app.
Verify your setup and initial page are working correctly.
Ensure a smooth start before adding more features.
npm run dev
http://localhost:3000
to see your new homepage.http://localhost:3000/template
after adding the guide content (next steps).Use VSCode’s built-in browser preview for quick testing.
Set up a basic Next.js page with Styled Components to kick off your project.
Use this as your starting point for any Next.js application with a clean, styled layout.
// app/page.tsx "use client"; import styled from "styled-components"; import Link from "next/link"; const PageContainer = styled.div` display: grid; grid-template-rows: 1fr auto; /* Main content and footer */ min-height: 100vh; width: 100%; /* Full viewport width */ background: #F7F4E9; /* Soft beige background */ padding: 2rem 0; /* Vertical padding only */ `; const MainContent = styled.main` display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2rem; text-align: center; padding: 2rem 1rem; /* Minimal horizontal padding for content */ @media (max-width: 768px) { padding: 1.5rem 1rem; gap: 1.5rem; } @media (max-width: 480px) { padding: 1rem; gap: 1rem; } `; const HeroTitle = styled.h1` font-size: 3.5rem; font-weight: 700; color: #000000; /* Black for title */ margin: 0; line-height: 1.2; @media (max-width: 768px) { font-size: 2.5rem; } @media (max-width: 480px) { font-size: 2rem; } `; const HeroSubtitle = styled.p` font-size: 1.25rem; color: #333333; /* Dark gray for subtitle */ max-width: 600px; line-height: 1.6; margin: 0; @media (max-width: 768px) { font-size: 1.1rem; max-width: 500px; } @media (max-width: 480px) { font-size: 1rem; max-width: 300px; } `; const ButtonContainer = styled.div` display: flex; gap: 1.5rem; flex-wrap: wrap; justify-content: center; margin-top: 2rem; @media (max-width: 768px) { gap: 1rem; margin-top: 1.5rem; } @media (max-width: 480px) { flex-direction: column; align-items: center; gap: 0.75rem; margin-top: 1rem; } `; const ActionButton = styled(Link)` padding: 0.75rem 1.5rem; font-size: 1rem; font-weight: 600; text-decoration: none; border-radius: 8px; background: #000000; /* Black for buttons */ color: #FFFFFF; /* White text */ transition: background 0.3s ease; cursor: pointer; &:hover { background: #333333; /* Dark gray on hover */ } @media (max-width: 768px) { padding: 0.6rem 1.2rem; font-size: 0.9rem; } @media (max-width: 480px) { padding: 0.5rem 1rem; width: 100%; max-width: 200px; } `; const Footer = styled.footer` display: flex; gap: 1.5rem; flex-wrap: wrap; justify-content: center; padding: 1rem; color: #333333; /* Dark gray for footer */ @media (max-width: 768px) { gap: 1rem; padding: 0.75rem; } @media (max-width: 480px) { flex-direction: column; align-items: center; gap: 0.75rem; padding: 0.5rem; } `; const FooterLink = styled.a` color: #333333; /* Dark gray for links */ text-decoration: none; font-size: 0.9rem; transition: color 0.3s ease; &:hover { color: #000000; /* Black on hover */ } @media (max-width: 768px) { font-size: 0.85rem; } @media (max-width: 480px) { font-size: 0.8rem; width: 100%; text-align: center; } `; export default function Home() { return ( <PageContainer> <MainContent> <HeroTitle>Welcome</HeroTitle> <HeroSubtitle> Kickstart your Next.js journey with this sleek template featuring Styled Components and the App Router. </HeroSubtitle> <ButtonContainer> <ActionButton href="/template">Explore Guide</ActionButton> <ActionButton href="https://nextjs.org/docs" target="_blank" rel="noopener noreferrer"> Next.js Docs </ActionButton> </ButtonContainer> </MainContent> <Footer> <FooterLink href="https://github.com" target="_blank" rel="noopener noreferrer"> GitHub </FooterLink> <FooterLink href="https://nextjs.org" target="_blank" rel="noopener noreferrer"> Next.js </FooterLink> <FooterLink href="https://styled-components.com" target="_blank" rel="noopener noreferrer"> Styled Components </FooterLink> </Footer> </PageContainer> ); }
Keep your initial page simple to build momentum—add complexity as needed.
The core files include a minimal layout, a toolbar for navigation, and this guide page. These provide the foundation for your Next.js app with App Router support.
Define the basic HTML structure and include a toolbar for all pages.
Use this to set up global styles and navigation across your app.
// app/layout.tsx import { ReactNode } from "react"; import StyledComponentsRegistry from "../components/styled-components-registry"; // Adjust to src/components/ after setup import Toolbar from "../components/Toolbar"; // Adjust to src/components/Toolbar after setup import ThemeWrapper from "../components/ThemeWrapper"; // Adjust to src/components/ThemeWrapper after setup import { CSSProperties } from "react"; export const metadata = { title: "Next.js Styled Template", description: "A Next.js starter with Styled Components", }; // Define inline styles with TypeScript typing for server compatibility const heroStyle: CSSProperties = { background: "#1a202c", // Hardcoded theme.colors.backgroundDark padding: "4rem 2rem", textAlign: "center", }; const heroTitleStyle: CSSProperties = { fontSize: "3rem", fontWeight: 700, color: "#f5f5d5", // Beige color margin: 0, }; export default function RootLayout({ children }: { children: ReactNode }) { return ( <html lang="en"> <body> <StyledComponentsRegistry> <ThemeWrapper> <Toolbar /> {children} </ThemeWrapper> </StyledComponentsRegistry> </body> </html> ); }
ThemeWrapper
for Styled Components compatibility.theme
can't be imported server-side here.Keep global styles minimal in layout.tsx
and let pages handle specific styling.
Provide a simple navigation bar with a toggleable menu for your app.
Ideal for adding consistent navigation without much overhead.
// src/components/Toolbar.tsx "use client"; import { useState } from "react"; import styled from "styled-components"; import Link from "next/link"; import { theme } from "../lib/theme"; const ToolbarContainer = styled.nav` display: flex; justify-content: space-between; align-items: center; padding: 1rem 2rem; background-color: ${theme.colors.backgroundDark}; position: fixed; top: 0; left: 0; width: 100%; z-index: 1000; box-sizing: border-box; min-height: 60px; `; const Logo = styled(Link)` font-size: 1.75rem; font-weight: 700; color: ${theme.colors.textDark}; text-decoration: none; transition: color 0.3s ease; &:hover { color: ${theme.colors.textLight}; } `; const NavLinks = styled.div` display: flex; gap: 1.5rem; align-items: center; @media (max-width: 768px) { display: none; /* Hide tabs on smaller screens */ } `; const NavLink = styled(Link)` font-size: 1.1rem; font-weight: 500; color: ${theme.colors.textDark}; text-decoration: none; transition: color 0.3s ease; &:hover { color: ${theme.colors.primary}; } `; const BurgerIcon = styled.div<{ $isOpen: boolean }>` display: none; /* Hidden on larger screens */ flex-direction: column; gap: 5px; cursor: pointer; padding: 0.5rem; @media (max-width: 768px) { display: flex; /* Show on smaller screens */ } div { width: 25px; height: 3px; background-color: ${theme.colors.textDark}; border-radius: 2px; transition: all 0.3s ease; } ${({ $isOpen }) => $isOpen && ` div:nth-child(1) { transform: rotate(45deg) translate(5px, 5px); } div:nth-child(2) { opacity: 0; } div:nth-child(3) { transform: rotate(-45deg) translate(6px, -6px); } `} `; const MobileMenu = styled.div<{ $isOpen: boolean }>` display: ${({ $isOpen }) => ($isOpen ? "flex" : "none")}; flex-direction: column; align-items: center; position: fixed; top: 60px; left: 0; width: 100%; background: ${theme.colors.backgroundContent}; padding: 2rem 0; z-index: 999; transition: opacity 0.3s ease; @media (min-width: 769px) { display: none; /* Hide on larger screens */ } `; const MobileNavLink = styled(Link)` font-size: 1.5rem; font-weight: 600; color: ${theme.colors.textDark}; text-decoration: none; padding: 1rem; width: 100%; text-align: center; transition: color 0.3s ease; &:hover { color: ${theme.colors.primary}; } `; export default function Toolbar() { const [isOpen, setIsOpen] = useState(false); const toggleMenu = () => { setIsOpen((prev) => !prev); }; return ( <> <ToolbarContainer> <Logo href="/">My App</Logo> <NavLinks> <NavLink href="/">Home</NavLink> <NavLink href="/template">Guide</NavLink> </NavLinks> <BurgerIcon $isOpen={isOpen} onClick={toggleMenu}> <div></div> <div></div> <div></div> </BurgerIcon> </ToolbarContainer> <MobileMenu $isOpen={isOpen}> <MobileNavLink href="/" onClick={toggleMenu}> Home </MobileNavLink> <MobileNavLink href="/template" onClick={toggleMenu}> Guide </MobileNavLink> </MobileMenu> </> ); }
Customize the toolbar’s colors in theme.ts
to match your brand.
Serve as the entry point to explore and learn about your template.
Use this page to document your project setup and showcase examples.
// app/template/page.tsx "use client"; import styled from "styled-components"; const PageWrapper = styled.div` padding: 80px 2rem 2rem; max-width: 900px; margin: 0 auto; text-align: center; `; const Heading = styled.h1` font-size: 2.5rem; font-weight: 700; color: #2d3748; margin-bottom: 1rem; `; const Description = styled.p` font-size: 1.25rem; color: #718096; line-height: 1.6; `; export default function TemplatePage() { return ( <PageWrapper> <Heading>Welcome</Heading> <Description> This is a simple Next.js starter template built with Styled Components. </Description> </PageWrapper> ); }
Keep this page updated as you add more features to your template.
These examples demonstrate common Next.js features with Styled Components, from routing to optimization, giving you a head start on typical use cases.
Showcase Next.js’s automatic routing based on file structure.
Use this for simple static pages like About or Contact.
Next.js uses file-based routing to create pages automatically based on the app/
directory structure. Here’s a simple example:
// app/page.tsx "use client"; export default function Home() { return <h1>Home Page</h1>; } // app/about/page.tsx "use client"; export default function About() { return <h1>About Page</h1>; } // app/contact/page.tsx "use client"; export default function Contact() { return <h1>Contact Page</h1>; }
app/page.tsx
→ /
(root route)app/about/page.tsx
→ /about
app/contact/page.tsx
→ /contact
Add files to app/
with a page.tsx
to create new routes instantly.
Name files intuitively to reflect their route (e.g., about.tsx
for /about
).
Render pages on the server for dynamic, SEO-friendly content.
Perfect for pages needing fresh data, like a dashboard or news feed.
// app/ssr/page.tsx import { Container, Title, Text } from "../../lib/styles"; export default async function SSR() { // Fetches data on the server for each request const res = await fetch("https://api.example.com/data", { cache: "no-store", // Ensures fresh data per request }); const data = await res.json(); return ( <Container> <Title>Server-Side Rendering</Title> <Text> Fetched on the server: {data.message || "Dynamic content here"} </Text> </Container> ); }
"use client"
) that runs on the server.fetch
retrieves fresh data for each request, making it true SSR.app/ssr/page.tsx
to access it at /ssr
.Keep SSR logic lightweight to avoid performance bottlenecks.
Pre-render pages at build time for fast, static content delivery.
Use this for blogs, docs, or landing pages that don’t change often.
// app/ssg/page.tsx import { Container, Title, Text } from "../../lib/styles"; export default async function SSG() { // Fetches data at build time const res = await fetch("https://jsonplaceholder.typicode.com/posts/1", { cache: "force-cache", // Ensures data is cached for static generation }); const post = await res.json(); return ( <Container> <Title>Static Site Generation</Title> <Text> Pre-rendered at build time: {post.title || "Static content here"} </Text> </Container> ); } // Optional: Incremental Static Regeneration (ISR) export const revalidate = 3600; // Revalidate every hour
fetch
with cache: "force-cache"
ensures data is fetched once during build.app/ssg/page.tsx
to access it at /ssg
as a static page.revalidate
enables Incremental Static Regeneration (ISR) to update content periodically.Add revalidate
in getStaticProps
for incremental static regeneration.
Create simple API endpoints within your Next.js app.
Great for form submissions or fetching data without a separate backend.
// app/api/hello/route.ts import { NextResponse } from "next/server"; export async function GET(request: Request) { const { searchParams } = new URL(request.url); const name = searchParams.get("name") || "World"; return NextResponse.json({ message: `Hello, ${name}!`, timestamp: new Date().toISOString(), }); }
/api/hello
.GET
handler responds with JSON data, e.g., {"message": "Hello, World!", "timestamp": "2025-04-11T..."})
.fetch("/api/hello?name=User")
or visit http://localhost:3000/api/hello
locally.Test endpoints locally at http://localhost:3000/api/...
during development.
Handle variable URLs like /post/[id]
with dynamic routing.
Use this for blog posts, product pages, or any parameterized content.
// app/blog/[slug]/page.tsx import { Container, Title, Text } from "../../../lib/styles"; export default async function BlogPost({ params }: { params: { slug: string } }) { // Fetch data based on the dynamic slug parameter const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.slug}`, { cache: "force-cache", // Static generation }); const post = await res.json(); return ( <Container> <Title>{post.title || "Blog Post"}</Title> <Text>{post.body || "Post content not found"}</Text> </Container> ); } // Pre-render specific slugs at build time export async function generateStaticParams() { return [ { slug: "1" }, { slug: "2" }, { slug: "3" }, ]; }
/blog/[slug]
, e.g., /blog/1
, /blog/2
.[slug]
parameter is passed via params.slug
to fetch specific data.generateStaticParams
pre-renders pages for slugs "1", "2", "3" at build time.fetch
with cache: "force-cache"
for static generation, or omit for SSR.Combine with getStaticPaths
for pre-rendered dynamic pages.
Optimize images with Next.js’s <Image>
component for better performance.
Ideal for image-heavy pages like galleries or portfolios.
// app/gallery/page.tsx import Image from "next/image"; import { Container, Title, Text } from "../../lib/styles"; export default function Gallery() { return ( <Container> <Title>Image Gallery</Title> <Text>Optimized images load fast and responsively.</Text> <Image src="/images/sample.jpg" // Place image in /public/images/ alt="Sample Image" width={600} height={400} priority // Loads immediately for above-the-fold content /> </Container> ); }
Image
component from next/image
optimizes images automatically.width
and height
ensure proper aspect ratio and resizing.priority
preloads the image for better performance on key visuals./public/
to serve them efficiently.Set priority
on above-the-fold images to load them first.
Add type safety to your Next.js app with TypeScript.
Use this for larger projects or teams to catch errors early.
// app/profile/page.tsx import { Container, Title, Text } from "../../lib/styles"; // Define a TypeScript interface for props interface UserProfile { name: string; email: string; role: "admin" | "user"; } export default function Profile({ user }: { user: UserProfile }) { return ( <Container> <Title>{user.name}’s Profile</Title> <Text> Email: {user.email} | Role: {user.role} </Text> </Container> ); } // Example usage with static data (could be fetched) export async function getServerSideProps() { const user: UserProfile = { name: "Jane Doe", email: "jane@example.com", role: "admin", }; return { props: { user } }; }
UserProfile
interface enforces type safety for the user
prop.email
).Leverage VSCode’s IntelliSense with TypeScript for faster coding.
Welcome to the Prompted Chatbot! How can I assist you today?