0

In Remix, how do you return json first and then redirect after a success call on the API?

export const action: ActionFunction = async ({
    request,
}: ActionFunctionArgs) => {
    const formData = await request.formData();

    try {
        const { project } = await sampleAPICall(formData);
        return json({
            title: "Success",
            message: "Yehey!",
        });

        throw redirect(`/${project?.id}`); 
    } catch (error) {
        return json({
            variant: "destructive",
            message: "Oh no....",
        });
    }
};
3
  • Related: stackoverflow.com/q/77725648/8690857
    – Drew Reese
    Commented Apr 23, 2024 at 15:15
  • @DrewReese. Great. Is there any difference performance wise between your answer and the one answered below by Kiliman?
    – Joseph
    Commented Apr 23, 2024 at 16:12
  • No, it's basically the same answer. Not close enough to be marked a true duplicate, but provides a bit more detail. I like Kiliman's session flash suggestion. There's a ton of overlap in features and shared code between Remix-run and the client-side react-router libraries they also maintain, one of them being the Data APIs and routers and loaders, etc.
    – Drew Reese
    Commented Apr 23, 2024 at 16:45

2 Answers 2

1

Redirects can't have a body, so you can't include a JSON payload in a redirect. If you want to display a message on the redirected route, you would typically use session flash.

https://remix.run/docs/en/main/utils/sessions#sessionflashkey-value

import { commitSession, getSession } from "../sessions";

export async function action({
  params,
  request,
}: ActionFunctionArgs) {
  const session = await getSession(request.headers.get("Cookie"));
  const formData = await request.formData;
  const { project } = await sampleAPICall(formData);
  session.flash("message", { title: "Success", message: "Yehey!" });

  return redirect("`/${project?.id}`, {
    headers: {
      "Set-Cookie": await commitSession(session),
    },
  });
}

Then in your project route, you can display the message from flash

import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { getSession, commitSession } from "./sessions";

export async function loader({ request } LoaderFunctionArgs) {
  const session = await getSession(request.headers.get("Cookie"));
  const message = session.get("message") || null;

  return json(
    { message },
    {
      headers: {
        // only necessary with cookieSessionStorage
        "Set-Cookie": await commitSession(session),
      },
    }
  );
}

export default function Component() {
  const { message } = useLoaderData<typeof loader>();

  return (
    <div>
      {message ? (
        <div className="flash">{message.title} {message.message}</div>
      ) : null}
    </div>
  );
}
4
  • Is this also ok? what's the disadvantage of this? stackoverflow.com/questions/77725648/…
    – Joseph
    Commented Apr 23, 2024 at 16:39
  • The Remix/Flash solution is how you handle it server-side. The React Router one shows how to do it on the client (where you return the redirect URL and call navigate in your component). This is basically what I said in the comment to Ilia's answer below.
    – Kiliman
    Commented Apr 23, 2024 at 21:59
  • Thank you. But is there an advantage doing it server side? Is it better? Is it faster performance wise?
    – Joseph
    Commented Apr 24, 2024 at 3:24
  • If you're already using Remix, you should probably do it server-side. The nice thing is that it will work whether you have JavaScript on the client or not.
    – Kiliman
    Commented Apr 25, 2024 at 13:03
1

I think you need to structure your code to handle the redirection separately from the JSON response. You cannot have code that executes after a return statement.

export const action: ActionFunction = async ({
    request,
    redirect,
}: ActionFunctionArgs) => {
    const formData = await request.formData();

    try {
        const { project } = await sampleAPICall(formData);
        return json({
            title: "Success",
            message: "Yehey!",
            redirectTo: `/${project?.id}`, // Include redirection URL in JSON response
        });
    } catch (error) {
        return json({
            variant: "destructive",
            message: "Oh no....",
        });
    }
};

export const loader: LoaderFunction = ({ json }) => {
    return json();
};

Hopefully I was able to help! Best of luck! :)

1
  • 1
    You should also note that since the redirect is not happening on the server, you'll need to handle that in the UI component. const navigate = useNavigate(); const data = useActionData(); if (data?.redirectTo) navigate(data.redirectTo);
    – Kiliman
    Commented Apr 23, 2024 at 13:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.