Next.js Discord

Discord Forum

router.push() waits for server action to resolve before navigation

Unanswered
Cuban Crocodile posted this in #help-forum
Open in Discord
Cuban CrocodileOP
I'm using a server action on submit of a form in a client component. When I submit the form, I want to navigate to a page, to display a loading state while waiting for the response from the server action. I have router.push('route') as the first line in my onSubmit function... but the navigation only takes place once the server action promise is resolved. I have tested this by adding a timeout to the server action.

Is this the expected behaviour? Is there a workaround?

29 Replies

if using server action, you could just use redirect inside...
@Cuban Crocodile did you end up getting something to work?
@riský if using server action, you could just use redirect inside...
Cuban CrocodileOP
redirect stops the rest of the server action function from running. I want to go to the route the second the user submits the form, not when the server action is finished.
Cuban CrocodileOP
Found a bit of a workaround by writing an inline function for the onSubmit form event, adding the router.push() there first, and then calling the function that calls the server action 🙄
but the server action's response is the rsc payload... i don't see how .push is that much better...
maybe it has better ux in the loading...
Cuban CrocodileOP
because the content isn't being displayed in the same component
@riský maybe it has better ux in the loading...
Cuban CrocodileOP
yeh this is the main reason - I want the loading state to be in the cart component, not the component that calls the action
ahhh, can you try with useState and just toggle that at the same time as server action (that is what i do)
Cuban CrocodileOP
just seems a shame when rsc/server actions is all about not needing to deal with state
well imo that isnt that much state and it gives way better ux (instant loading)
Cuban CrocodileOP
Yes, I'm still undecided whether to leave the cart as a parallel route, or just use state. I like the "back button" functionality you get with routes, but you give up the control and snappier UX.
hmm can't you have both?
i personally just use router.refresh and my ocn endpoints as server actions seem too weird right now
but they have worked decent when i tried them
@Cuban Crocodile Click to see attachment
ahh i just realised that makes sense... because server actions have to run before the response, but .push can already load it and then done at similar times
@riský ahh i just realised that makes sense... because server actions have to run before the response, but .push can already load it and then done at similar times
Cuban CrocodileOP
but isn't it strange how this doesn't work? addToCart is the server action, this function is running in a client component, passed to the onSubmit in the last screenshot
it must recognise that the function contains a server action so the router.push() is only run after the server action returns a response. You can call setState and it's run immediately though.
I even tested moving the addToCart into a wrapper function and it behaved the same way
hmm intresting
Cuban CrocodileOP
I hope it's a bug, because I think they should be following the normal rules of execution. If I wanted to wait for the server action to respond first, I'd move the router.push() to the end of the function!
it is experimental for a reason
i would see if they have a github issue, and if not make your own because then you can suggest it to be fixed or feature...
Cuban CrocodileOP
Thanks for the discussion 🙌
yeah... i didn't realise they were so fancy/weird...
Basset Bleu de Gascogne
bumping in case you learned anything new @Cuban Crocodile. I'm generally finding that if I perform the following:

router.push("/some_route") async_server_action()

it waits to resolve.

If I do:
async_server_action() router.push("/some_route")

Then it navigates to the new route and the server action resolves in the background. Weird behavior. Did you find explanation for this?
Australian Cattle Dog
I'm also experirencing this delay when trying to router.back().
Putting the router call after the server action still has the delay.
Also, wrapping the call in a startTransition doesn't change anything.

'use client';

import { useRouter } from 'next/navigation';
import { Button } from '@/components/ui/button';
import deleteTaskAction from '@/actions/deleteTaskAction';

const DeleteTaskButton = ({ id }: { id: number }) => {
  const router = useRouter();

  return (
    <Button
      onClick={async () => {
        deleteTaskAction(id);
        router.back();
      }}
      variant="outline"
    >
      Delete
    </Button>
  );
};

export default DeleteTaskButton;