ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

A Small Dive into Next.js: Modal, DB, and First Impressions

By Peter, backend engineer at Unifa.

As someone who mainly works with Ruby on Rails and Vue.js in my day-to-day projects, I always enjoy exploring new frontend technologies in my spare time. Recently, I decided to give Next.js a try — not just to see what it’s like, but also to connect it to a local database and fetch some data, just to get a feel for how the whole flow works in a Next.js setup.

Next.js is great because it:

  • Supports hybrid rendering — you can pre-render pages on the server (SSR/SSG) but still do dynamic things on the client
  • Simplifies navigation — just drop your page in the pages/ folder and it works
  • Makes API integration easy — write server functions as part of the frontend project
  • Helps you scale — with tools like Backend for Frontend (BFF)

Let’s Build a Modal in Next.js

Here’s a simple but powerful example: showing post content in a modal when users click a title.

Step 1: Create Your Next.js Project

npx create-next-app@latest my-next-modal

cd my-next-modal

npm install react-modal prisma @prisma/client

npx prisma init

Step 2: Set Up a Local Database with Prisma

Replace the contents of prisma/schema.prisma with:

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id    Int    @id @default(autoincrement())
  title String
  body  String
}
npx prisma migrate dev --name init

This will generate a local SQLite database (dev.db) with a Post table. You can populate it using Prisma Studio:

npx prisma studio

Step 3: Configure Modal for Accessibility

Inside pages/_app.js:

// pages/_app.js

import { useEffect } from 'react'
import Modal from 'react-modal'
import '../styles/globals.css'

Modal.setAppElement('#__next') // Ensure modal accessibility

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

This ensures screen readers won’t read the background while the modal is open.

Step 4: Add API Routes for Fetching Posts

Create the following files:

pages/api/posts.js

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export default async function handler(req, res) {
  const posts = await prisma.post.findMany()
  res.status(200).json(posts)
}

pages/api/posts/[id].js

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

export default async function handler(req, res) {
  const { id } = req.query
  const post = await prisma.post.findUnique({
    where: { id: parseInt(id) },
  })

  if (post) {
    res.status(200).json(post)
  } else {
    res.status(404).json({ error: 'Post not found' })
  }
}

Step 5: Build the List + Modal Component

Replace pages/index.js with:

import { useState } from 'react'
import Modal from 'react-modal'

export default function Home({ posts }) {
  const [isOpen, setIsOpen] = useState(false)
  const [activePost, setActivePost] = useState(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  async function openModal(id) {
    setIsOpen(true)
    setLoading(true)
    setError(null)
    try {
      const res = await fetch(`/api/posts/${id}`)
      if (!res.ok) throw new Error('Failed to fetch')
      const post = await res.json()
      setActivePost(post)
    } catch (e) {
      setError(e.message)
    } finally {
      setLoading(false)
    }
  }

  function closeModal() {
    setIsOpen(false)
    setActivePost(null)
  }

  return (
    <div style={{ padding: 20 }}>
      <h1>Posts</h1>
      <ul>
        {posts.map((p) => (
          <li key={p.id} style={{ marginBottom: 8 }}>
            <button
              onClick={() => openModal(p.id)}
              style={{
                background: 'none',
                border: 'none',
                color: '#0070f3',
                cursor: 'pointer',
                padding: 0,
              }}
            >
              {p.title}
            </button>
          </li>
        ))}
      </ul>

      <Modal
        isOpen={isOpen}
        onRequestClose={closeModal}
        style={{
          overlay: { backgroundColor: 'rgba(0,0,0,0.5)' },
          content: { inset: '20% 10%', padding: 20 },
        }}
      >
        <button onClick={closeModal} style={{ float: 'right' }}>Close</button>
        {loading && <p>Loading…</p>}
        {error && <p style={{ color: 'red' }}>Error: {error}</p>}
        {activePost && (
          <>
            <h2>{activePost.title}</h2>
            <p>{activePost.body}</p>
          </>
        )}
      </Modal>
    </div>
  )
}

export async function getStaticProps() {
  const res = await fetch('http://localhost:3000/api/posts') // or use env var
  const posts = await res.json()
  return {
    props: { posts },
    revalidate: 10,
  }
}

Why This Example Matters

This small example captures what I love about Next.js:

  • React works seamlessly for building the UI.

  • Server-side rendering comes built-in by default.

  • It’s easy to fetch data directly from a local database without setting up a separate backend.

  • Routes are automatically handled — no need for manual setup.

And all of this makes building dynamic, responsive web apps way easier

Is Next.js for You?

Personally, I think Next.js is a great fit if you’re after things like:

  • Fast first-page loads that feel like a traditional backend-rendered app

  • Smooth, SPA-like user interactions (without manually wiring up AJAX calls)

  • Zero-config routing that just works

  • A simpler deployment flow where one app handles both frontend and backend

That’s what really drew me in — it feels modern, but also practical.

Even if you already have an existing site, it’s totally possible to introduce Next.js gradually. You can start with just one page, or build out a section using micro frontends. It doesn’t have to be an all-or-nothing switch.

Of course, it’s not a silver bullet. If you’re building a pure API backend, or something super dynamic that doesn’t need SEO (like a real-time dashboard), maybe it’s not the best fit. Same goes for offline-first mobile apps. But for most modern web apps? I’d say it’s absolutely worth trying.

Want to Try It Yourself?

The official tutorial at nextjs.org/learn is excellent. I learned a lot from it and built the modal example above based on what I picked up there.

Hope this post helps you understand why I enjoy working with Next.js — and maybe inspires you to try building something with it too!


Unifa is actively recruiting, please check our website for details:

unifa-e.com