¿Tiene shadcn ui algún popup dialog que adentro pueda tener cualquier HTML y JSX?
Sí, shadcn/ui incluye un componente de Dialog que funciona como un popup modal y dentro de su contenido (DialogContent) podés renderizar cualquier HTML o JSX sin restricciones. Está construido sobre los primitives de Radix UI, lo que garantiza accesibilidad, manejo de foco y soporte para teclado.
🔹 Cómo funciona el Dialog en shadcn/ui
Componentes principales:
Dialog: contenedor raíz.DialogTrigger: define el elemento que abre el popup.DialogContent: el área del popup donde podés poner cualquier HTML/JSX.DialogHeader,DialogTitle,DialogDescription: opcionales para estructurar el contenido.DialogFooter: opcional para botones de acción.DialogClose: para cerrar el popup.
Flexibilidad: Dentro de
DialogContentpodés incluir formularios, inputs, listas, imágenes, componentes personalizados, incluso otros hooks de React. No hay limitación: es un contenedor React normal.
🔹 Ejemplo práctico
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
export function SoapDialog() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Abrir popup</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Editar jabón</DialogTitle>
<DialogDescription>
Podés modificar cualquier dato del jabón aquí.
</DialogDescription>
</DialogHeader>
{/* Aquí podés poner cualquier HTML/JSX */}
<form className="space-y-4">
<input type="text" placeholder="Nombre" className="border p-2 w-full" />
<textarea placeholder="Descripción" className="border p-2 w-full" />
</form>
<DialogFooter>
<Button type="submit">Guardar</Button>
<DialogClose asChild>
<Button variant="secondary">Cancelar</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
👉 Dentro de DialogContent podés poner cualquier cosa: inputs, tablas, componentes React, incluso otro layout complejo.
🔹 Variantes disponibles
Alert Dialog (confirmaciones destructivas).
Scrollable Dialog (contenido largo con scroll).
Fullscreen Dialog (ocupa toda la pantalla).
Animaciones con Motion para transiciones suaves.
✨ Conclusión
Sí, shadcn/ui tiene un
Dialogque actúa como popup modal.Dentro de
DialogContentpodés renderizar cualquier HTML o JSX.Es accesible, flexible y extensible, ideal para formularios, confirmaciones o contenido complejo.
¿Querés que te arme un ejemplo donde el Dialog contenga un componente React completo (por ejemplo, un formulario con validación y estado) para ver cómo se integra dentro del popup?
Genial, Gabriel 🙌. Te armo un ejemplo completo donde el Dialog de shadcn/ui contiene un formulario React con validación y estado, para que veas cómo se integra dentro del popup:
🔹 Ejemplo: Formulario dentro de un Dialog
import * as React from "react"
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button"
export function SoapDialogForm() {
const [name, setName] = React.useState("")
const [description, setDescription] = React.useState("")
const [error, setError] = React.useState("")
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
if (!name.trim()) {
setError("El nombre es obligatorio")
return
}
// Aquí podrías enviar al back end
console.log("Datos enviados:", { name, description })
setError("")
}
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Editar jabón</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Editar jabón</DialogTitle>
<DialogDescription>
Modificá los datos y guardalos en el back end.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="text"
placeholder="Nombre"
value={name}
onChange={(e) => setName(e.target.value)}
className="border p-2 w-full"
/>
<textarea
placeholder="Descripción"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="border p-2 w-full"
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
<DialogFooter>
<Button type="submit">Guardar</Button>
<DialogClose asChild>
<Button variant="secondary">Cancelar</Button>
</DialogClose>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}
🔹 Qué pasa aquí
El
Dialogabre un popup modal.Dentro de
DialogContenthay un formulario React con estado (useState).Se valida que el nombre no esté vacío antes de enviar.
Podés reemplazar el
console.logpor una llamada al back end (fetch,axios, o React Query).El
DialogClosepermite cerrar el popup desde un botón.
✨ Con esto ves que el Dialog de shadcn/ui es totalmente flexible: podés meter cualquier HTML/JSX, manejar estado local, validaciones y lógica de negocio sin limitaciones.
¿Querés que te muestre cómo integrar este formulario con React Query para que al guardar se actualice automáticamente la lista de jabones en tu UI?
Perfecto, Gabriel 🙌. Vamos a integrar el formulario dentro del Dialog de shadcn/ui con React Query, de modo que al guardar se actualice automáticamente la lista de jabones en tu UI.
🔹 Repositorio de datos
// repositories/soapRepository.ts
export async function getSoaps() {
const res = await fetch("/api/soaps");
if (!res.ok) throw new Error("Error al obtener jabones");
return res.json();
}
export async function updateSoap(id: string, data: any) {
const res = await fetch(`/api/soaps/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error("Error al actualizar jabón");
return res.json();
}
🔹 Hook con React Query
// hooks/useSoaps.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { getSoaps, updateSoap } from "@repositories/soapRepository";
export function useSoaps() {
return useQuery({
queryKey: ["soaps"],
queryFn: getSoaps,
});
}
export function useUpdateSoap() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: string; data: any }) =>
updateSoap(id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["soaps"] }); // ← refresca la lista
},
});
}
🔹 Formulario dentro del Dialog
import * as React from "react";
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { useUpdateSoap } from "@hooks/useSoaps";
export function SoapDialogForm({ soap }: { soap: any }) {
const [name, setName] = React.useState(soap.nombre);
const [description, setDescription] = React.useState(soap.descripcion);
const mutation = useUpdateSoap();
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
mutation.mutate({ id: soap.id, data: { nombre: name, descripcion: description } });
}
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Editar {soap.nombre}</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Editar jabón</DialogTitle>
<DialogDescription>
Modificá los datos y guardalos en el back end.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="text"
placeholder="Nombre"
value={name}
onChange={(e) => setName(e.target.value)}
className="border p-2 w-full"
/>
<textarea
placeholder="Descripción"
value={description}
onChange={(e) => setDescription(e.target.value)}
className="border p-2 w-full"
/>
<DialogFooter>
<Button type="submit">Guardar</Button>
<DialogClose asChild>
<Button variant="secondary">Cancelar</Button>
</DialogClose>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}
🔹 Lista de jabones que se refresca automáticamente
import { useSoaps } from "@hooks/useSoaps";
import { SoapDialogForm } from "@components/SoapDialogForm";
export default function SoapList() {
const { data: soaps, isLoading } = useSoaps();
if (isLoading) return <p>Cargando jabones...</p>;
return (
<div className="space-y-4">
{soaps.map((soap: any) => (
<div key={soap.id} className="border p-4">
<h2>{soap.nombre}</h2>
<p>{soap.descripcion}</p>
<SoapDialogForm soap={soap} />
</div>
))}
</div>
);
}
✨ Flujo completo
SoapListcarga la lista de jabones desde el back end.Cada jabón tiene un botón que abre un
Dialogcon el formulario.Al guardar, se dispara la mutación con React Query.
onSuccessinvalida la query → se refresca la lista automáticamente.La UI siempre queda sincronizada con el back end.
¿Querés que te prepare también un diagrama visual del flujo mostrando cómo el Dialog dispara la mutación y cómo React Query refresca la lista de jabones?
Muy buena pregunta, Gabriel 👌. En shadcn/ui (que se apoya en los Dialog primitives de Radix UI), las variantes como Scrollable y Fullscreen no son “tipos distintos de componente”, sino combinaciones de estilos y props aplicadas sobre el mismo DialogContent.
🔹 Cómo funcionan las variantes
Scrollable Dialog: se logra aplicando clases utilitarias (ej.
max-h-[80vh] overflow-y-auto) alDialogContentpara que el contenido tenga scroll interno.Fullscreen Dialog: se logra aplicando clases que expanden el
DialogContenta ocupar toda la pantalla (ej.w-screen h-screen max-w-none).
🔹 Combinación posible
Sí, podés combinarlas. Por ejemplo, un DialogContent que ocupe toda la pantalla y además tenga scroll interno:
<DialogContent className="w-screen h-screen max-w-none overflow-y-auto">
<DialogHeader>
<DialogTitle>Jabón en pantalla completa</DialogTitle>
<DialogDescription>
Este contenido ocupa toda la pantalla y se puede scrollear.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 p-6">
{/* Aquí podés poner cualquier JSX largo */}
<p>Sección 1...</p>
<p>Sección 2...</p>
<p>Sección 3...</p>
{/* etc */}
</div>
</DialogContent>
👉 Esto combina ambas variantes:
Fullscreen: ocupa toda la pantalla.
Scrollable: si el contenido es más largo que la altura, aparece scroll interno.
✨ Conclusión
Las variantes en shadcn/ui son patrones de estilo, no componentes separados.
Podés mezclarlas libremente aplicando las clases adecuadas.
Un
DialogContentfullscreen puede ser scrollable sin problemas.
¿Querés que te arme un ejemplo extendido con contenido muy largo (ej. lista de productos) para que veas cómo se comporta el scroll dentro de un fullscreen dialog?
No hay comentarios:
Publicar un comentario