Next.js Discord

Discord Forum

Modals in NextJS

Answered
Argentine hake posted this in #help-forum
Open in Discord
Argentine hakeOP
Hello everyone,

I am trying to create a simple confirmation modal that pops up whenever a user tries to purchase an item. After a small bit of research it seems like intercepting routes is the main way of working with modals in Next. However, the problem is that they rely on navigating to a different page (without re-rendering the UI). For example, when looking at the nextgram example (https://github.com/vercel/nextgram), we can see that upon clicking one of the items, the client is redirected to another page. If we then refresh without navigating back to the home page, we can see that it ends up rendering a completely different page. Is there any way to go around this / prevent this?
Answered by Sun bear
glad I could help, the jotai approach is defenitely better imo for something like this. it reduces the code to just this:

// atoms/product-modal.ts
import {atom} from "jotai"

const openAtom = atom<boolean>(false)
const product = atom<ProductInfoType | null>(null)

export function useProductModal() {
  const [open, setOpen] = useAtom(openAtom)
  const [product, setProduct] = useAtom(productAtom)

  return {open, setOpen, product, setProduct}
}


// components/overlay/product-modal.tsx

export function ProductModal() {
  const {open, product, setOpen} = useProductModal()

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogContent>
        { /* Display product based on "product" */}
      </AlertDialogContent>
    </AlertDialog>
  )
}


// components/product-card.tsx

export function ProductCard(props) {
  const {setOpen, setProduct} = useProductModal()
  
  function onBuy() {
    setProduct(props.product)
    setOpen(true)
  }

  return (
    <div>
      { /* Your card looks */ }
      <button onClick={onBuy}>Buy Now</button>
    </div>
  )
}
View full answer

22 Replies

Sun bear
you can use the built in [dialog component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) or go with a pre built primitive solution like [radix alert dialog](https://www.radix-ui.com/themes/docs/components/alert-dialog) or even [shadcn ui alert-dialog](https://ui.shadcn.com/docs/components/alert-dialog) built on top of radix one.
@Sun bear you can use the built in [dialog component](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) or go with a pre built primitive solution like [radix alert dialog](https://www.radix-ui.com/themes/docs/components/alert-dialog) or even [shadcn ui alert-dialog](https://ui.shadcn.com/docs/components/alert-dialog) built on top of radix one.
Argentine hakeOP
Thank you for responding! The problem isn't about creating said component, but rather managing the states and interactions between the modal and other elements. In my case I have a form that opens the confirmation modal. If the user confirms, it should execute a server action based on the data from the original form (item card)
Sun bear
you can use react context or state management libraries for this.
@Sun bear you can use react context or state management libraries for this.
Argentine hakeOP
That's fine for passing the object ref, however, I don't feel comfortable with passing API data to a client component
Sun bear
depending on the app data you can choose only the non-sensitive information to pass through the client.
either way you need to send something to the client on order to give the user a feedback
Argentine hakeOP
I guess that's true. Nonetheless, I am a little confused on how to achieve this using the Context API and for example the shadcn alert
Let me elaborate, I have a list of item cards that I fetch from an API. Upon clicking the button in a form within the item card I want to open the modal with the data from that card. How do I achieve this?
Sun bear
Have each card contain its own modal and pass the needed information as props
@Sun bear Have each card contain its own modal and pass the needed information as props
Argentine hakeOP
Wouldn't that be incredibly inefficient?
Sun bear
there is one other way but it's a little complicated
@Argentine hake Wouldn't that be incredibly inefficient?
Sun bear
this will work unless you have a lottt of products
Argentine hakeOP
Hm...
Okay
Well thank you for the help! I'll try and see if I can figure it out
Sun bear
this way you only render 1 modal
I highly recommend something like jotai for this, but you can also use react context https://jotai.org/
@Sun bear this way you only render 1 modal
Argentine hakeOP
Thank you!
@Sun bear I highly recommend something like jotai for this, but you can also use react context https://jotai.org/
Argentine hakeOP
I think I'll stick with the context API for now but I'll definitely look into Jotai for my next project
Sun bear
glad I could help, the jotai approach is defenitely better imo for something like this. it reduces the code to just this:

// atoms/product-modal.ts
import {atom} from "jotai"

const openAtom = atom<boolean>(false)
const product = atom<ProductInfoType | null>(null)

export function useProductModal() {
  const [open, setOpen] = useAtom(openAtom)
  const [product, setProduct] = useAtom(productAtom)

  return {open, setOpen, product, setProduct}
}


// components/overlay/product-modal.tsx

export function ProductModal() {
  const {open, product, setOpen} = useProductModal()

  return (
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogContent>
        { /* Display product based on "product" */}
      </AlertDialogContent>
    </AlertDialog>
  )
}


// components/product-card.tsx

export function ProductCard(props) {
  const {setOpen, setProduct} = useProductModal()
  
  function onBuy() {
    setProduct(props.product)
    setOpen(true)
  }

  return (
    <div>
      { /* Your card looks */ }
      <button onClick={onBuy}>Buy Now</button>
    </div>
  )
}
Answer
Argentine hakeOP
Thank you again @Sun bear, have a wonderful day!