Workshop Next.js UDINUS

1. Hasil Akhir Workshop

  • Paham alur kerja Next.js App Router.

  • Bisa membaca dan memodifikasi tiap file utama project.

  • Bisa menjalankan project di lokal dengan NPM.

  • Bisa build dan deploy ke Vercel.

2. Prasyarat Instalasi

2.1 Tool yang wajib ada

  • Node.js 20+ (disarankan 20 LTS).

  • NPM (otomatis ikut saat instal Node.js).

  • Git.

  • VS Code.

2.2 Cek versi tool

node -v
npm -v
git --version

3. Membuat dan Menjalankan Project

3.1 Create project

npx create-next-app@latest belajar-next-udinus

3.2 Masuk ke project dan install package

cd belajar-next-udinus
npm install

3.3 Jalankan local development

npm run dev

Buka http://localhost:3000.

4. Struktur Folder Project

app/
  globals.css
  layout.tsx
  page.tsx
  components/
    navbar.tsx
    footer.tsx
  section/
    hero.tsx
    skills.tsx
    education.tsx
    contact.tsx
  project/
    page.tsx
  ui/
    button.tsx
    card.tsx
public/
  images/

5. Penjelasan Konsep Dasar Next.js

5.1 app/layout.tsx

Ini adalah layout global. Navbar dan Footer ditempatkan di sini supaya otomatis muncul di semua halaman.

5.2 app/page.tsx

Ini adalah halaman Home. File ini hanya menyusun section agar kode lebih bersih.

5.3 app/project/page.tsx

Ini route tambahan untuk halaman Project di URL /project.

6. Full Kode Tiap File Utama (Sesuai Project)

6.1 app/layout.tsx

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Navbar from "./components/navbar";
import Footer from "./components/footer";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "{Nama Kalian}",
  description: "Website Portofolio {Nama Kalian}",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html
      lang="en"
      className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
    >
      <body className="min-h-full flex flex-col items-center px-5">
        <Navbar />
        <div className="w-full max-w-xl py-20 min-h-screen">{children}</div>
        <Footer />
      </body>
    </html>
  );
}

6.2 app/page.tsx

import Hero from "./section/hero";
import Skills from "./section/skills";
import Education from "./section/education";
import Contact from "./section/contact";

export default function Home() {
  return (
    <>
      <Hero />
      <Skills />
      <Education />
      <Contact />
    </>
  );
}

6.3 app/components/navbar.tsx

import Link from "next/link";

export default function Navbar() {
  return (
    <div className="h-14 w-full border-b border-zinc-800 flex px-5 items-center justify-between fixed bg-background">
      <Link href="/">
        <h1 className="text-lg font-semibold tracking-tight">
          {Nama Kalian}
        </h1>
      </Link>
      <div className="flex gap-x-5">
        <Link href="/" className="hover:underline">
          Home
        </Link>
        <Link href="/project" className="hover:underline">
          Project
        </Link>
      </div>
    </div>
  );
}

6.4 app/components/footer.tsx

export default function Footer() {
  return (
    <div className="h-14 w-full border-t border-zinc-800 flex px-5 items-center justify-center">
      <h1>Made With 💖 By {Nama Kalian}</h1>
    </div>
  );
}

6.5 app/section/hero.tsx

import Image from "next/image";

export default function Hero() {
  return (
    <div className="flex flex-col items-center mt-10">
      <div className="w-44 h-44 rounded-full overflow-hidden">
        <Image
          src="/images/face.jpg"
          alt="face"
          width={200}
          height={200}
          className="w-full h-full object-cover object-center"
        />
      </div>
      <h1 className="text-xl sm:text-3xl font-bold mt-5 tracking-tighter">
        👋{Nama Kalian}🚀
      </h1>
      <p className="text-center w-full max-w-md mt-5">
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut, doloribus?
        Ab temporibus explicabo saepe perspiciatis, eligendi quod minus error
        ullam tempora quas accusantium itaque placeat mollitia, nemo magni, quam
        voluptas!
      </p>
    </div>
  );
}

6.6 app/section/skills.tsx

export default function Skills() {
  return (
    <div className="mt-20">
      <h1 className="font-semibold text-xl">⭐Skills</h1>
      <div className="flex flex-wrap mt-5 gap-3">
        <div className="rounded-lg bg-zinc-800 p-2 text-sm font-semibold">
          HTML
        </div>
        <div className="rounded-lg bg-zinc-800 p-2 text-sm font-semibold">
          CSS
        </div>
        <div className="rounded-lg bg-zinc-800 p-2 text-sm font-semibold">
          Tailwind
        </div>
        <div className="rounded-lg bg-zinc-800 p-2 text-sm font-semibold">
          Next.js
        </div>
      </div>
    </div>
  );
}

6.7 app/section/education.tsx

export default function Education() {
  return (
    <div className="mt-20">
      <h1 className="font-bold text-xl">🎓Education</h1>
      <div className="mt-5">
        <h1 className="font-bold">
          ✨Universitas Diponegoro - Teknik Komputer
        </h1>
        <p className="mt-3">
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Similique,
          nisi voluptatibus nostrum nihil mollitia quae dolor debitis, quidem
          consectetur totam illum repellendus dolorum aliquid? Deserunt
          repellendus illum ut optio modi?
        </p>
      </div>
      <div className="mt-5">
        <h1 className="font-bold">✨SMAN 2 Semarang</h1>
        <p className="mt-3">
          Lorem, ipsum dolor sit amet consectetur adipisicing elit. Similique,
          nisi voluptatibus nostrum nihil mollitia quae dolor debitis, quidem
          consectetur totam illum repellendus dolorum aliquid? Deserunt
          repellendus illum ut optio modi?
        </p>
      </div>
    </div>
  );
}

6.8 app/section/contact.tsx

import Button from "../ui/button";
import Link from "next/link";

export default function Contact() {
  return (
    <div className="mt-20">
      <h1 className="font-bold text-xl">📞Contact</h1>
      <div className="flex flex-wrap gap-3 mt-5">
        <Link
          href="https://www.linkedin.com/in/pramudya-diagusta/"
          target="_blank"
        >
          <Button>Linkedin</Button>
        </Link>
        <Button>Github</Button>
        <Button>Instagram</Button>
        <Button>Email</Button>
      </div>
    </div>
  );
}

6.9 app/ui/button.tsx

export default function Button({ children }: any) {
  return (
    <button className="bg-white p-2 rounded-xl cursor-pointer text-black hover:bg-zinc-300 font-bold">
      {children}
    </button>
  );
}

6.10 app/ui/card.tsx

import Image from "next/image";
import Button from "./button";
import Link from "next/link";

export default function Card({
  title,
  image,
  description,
  website,
  github,
}: any) {
  return (
    <div className="bg-zinc-900 rounded-xl p-4 space-y-4">
      <h1 className="font-bold">{title}</h1>
      <div className="aspect-video w-full rounded overflow-hidden">
        <Image
          src={image}
          alt="face"
          width={200}
          height={200}
          className="w-full h-full object-cover object-center"
        />
      </div>
      <p className="text-sm">{description}</p>
      <div className="space-x-2">
        <Link href={website}>
          <Button>Website</Button>
        </Link>
        <Link href={github}>
          <Button>Github</Button>
        </Link>
      </div>
    </div>
  );
}

6.11 app/project/page.tsx

import Card from "../ui/card";

export default function Page() {
  return (
    <div>
      <h1 className="font-semibold text-xl">🚀Projects</h1>
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-5">
        <Card
          title="Project 1"
          image="/images/project.png"
          description="Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, veritatis!"
          website="/"
          github="/"
        />
        <Card
          title="Project 2"
          image="/images/project.png"
          description="Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, veritatis!"
          website="/"
          github="/"
        />
        <Card
          title="Project 3"
          image="/images/project.png"
          description="Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, veritatis!"
          website="/"
          github="/"
        />
      </div>
    </div>
  );
}

6.12 app/globals.css

@import "tailwindcss";

:root {
  --background: #ffffff;
  --foreground: #171717;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --font-sans: var(--font-geist-sans);
  --font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

body {
  background: var(--background);
  color: var(--foreground);
  font-family: Arial, Helvetica, sans-serif;
}

7. Menjalankan Project, Lint, dan Build

7.1 Development

npm run dev

7.2 Lint

npm run lint

7.3 Build production

npm run build

7.4 Jalankan build di lokal

npm run start

8. Deployment ke Vercel

Pastikan di pc/laptop sudah terinstall Git. Jika belum bisa install di

https://git-scm.com/install/windows

Dan jika belum memiliki akun github bisa register disini:

https://github.com/signup

8.1 Push ke GitHub

git config --global user.name "username_github"
git config --global user.email "emailkamu@example.com"
git init
git add .
git commit -m "Initial commit: portfolio nextjs"
git branch -M main
git remote add origin https://github.com/USERNAME/NAMA-REPO.git
git push -u origin main

8.2 Deploy lewat dashboard Vercel

  1. Login/Register ke Vercel dengan akun GitHub.

https://vercel.com/

Screenshot 2026-04-07 154519
  1. Pilih Add New Project.

Screenshot 2026-04-07 154620
  1. Import repository project.

Screenshot 2026-04-07 155018
  1. Klik Deploy.

Screenshot 2026-04-07 155117