"use client"; import { createFalClient } from "@fal-ai/client"; import { useMemo, useState } from "react"; const fal = createFalClient({ proxyUrl: "/api/fal/proxy", // the built-int nextjs proxy // proxyUrl: 'http://localhost:3333/api/fal/proxy', // or your own external proxy }); type Image = { filename: string; subfolder: string; type: string; url: string; }; type ComfyOutput = { url: string; outputs: Record[]; images: Image[]; }; type ErrorProps = { error: any; }; function Error(props: ErrorProps) { if (!props.error) { return null; } return (
Error {props.error.message}
); } const DEFAULT_PROMPT = "a city landscape of a cyberpunk metropolis, raining, purple, pink and teal neon lights, highly detailed, uhd"; export default function ComfyTextToImagePage() { // @snippet:start("client.ui.state") // Input state const [prompt, setPrompt] = useState(DEFAULT_PROMPT); // Result state const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState(null); const [logs, setLogs] = useState([]); const [elapsedTime, setElapsedTime] = useState(0); // @snippet:end const image = useMemo(() => { if (!result) { return null; } return result; }, [result]); const reset = () => { setLoading(false); setError(null); setResult(null); setLogs([]); setElapsedTime(0); }; const getImageURL = (result: ComfyOutput) => { return result.outputs[9].images[0]; }; const generateImage = async () => { reset(); // @snippet:start("client.queue.subscribe") setLoading(true); const start = Date.now(); try { const { data } = await fal.subscribe( "comfy/fal-ai/text-to-image", { input: { prompt: prompt, }, logs: true, onQueueUpdate(update) { setElapsedTime(Date.now() - start); if ( update.status === "IN_PROGRESS" || update.status === "COMPLETED" ) { setLogs((update.logs || []).map((log) => log.message)); } }, }, ); setResult(getImageURL(data)); } catch (error: any) { setError(error); } finally { setLoading(false); setElapsedTime(Date.now() - start); } // @snippet:end }; return (

Comfy SDXL - Text to Image

setPrompt(e.target.value)} onBlur={(e) => setPrompt(e.target.value.trim())} />
{image && ( // eslint-disable-next-line @next/next/no-img-element )}

JSON Result

{`Elapsed Time (seconds): ${(elapsedTime / 1000).toFixed(2)}`}

              {result
                ? JSON.stringify(result, null, 2)
                : "// result pending..."}
            

Logs

              {logs.filter(Boolean).join("\n")}
            
); }