Challenges Encountered with Server Operations in Next.js v14 - Issue with Cookie Modification

I am currently working on a project using Next.js v14 and I have run into an issue with Server Actions, specifically when attempting to modify cookies. Despite meticulously following the documentation and ensuring that my functions are classified as Server Actions, I am still encountering an error. Here is a brief overview of my setup and the problem at hand:

Component (src/app/page.tsx):

import React from 'react';
import { cookies } from 'next/headers';
import { exampleAction } from './actions';

const serverActionCookies = async () => {
    'use server';
    cookies().set('name', 'Delba');
    cookies().delete('name');
};

const Test = async () => {
    await exampleAction();
    await serverActionCookies();
    return null;
};

const Page = () => (
    <>
        <Test />
        Test server action
    </>
);

export default Page;

Action (src/app/actions.ts):

'use server';

import { cookies } from 'next/headers';

export async function exampleAction() {
    cookies().set('name', 'Delba');
    cookies().delete('name');
}

Upon running this code snippet, neither exampleAction nor serverActionCookies behave as anticipated. Instead, I encounter the following error message:

Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options

Both functions are meant to be Server Actions, yet they do not appear to be recognized as such. I have thoroughly reviewed the documentation and my code structure, but I cannot seem to pinpoint what I may be overlooking or doing incorrectly.

Thank you for your assistance!

Answer №1

The Reason Why:

...HTTP does not allow setting cookies after streaming begins, so the .set() method must be used in a Server Action or Route Handler.

The issue with setting cookies arises from the flow of an HTTP response and React's new feature known as Streaming.

When a Next.js server component runs, it has already generated the HTTP response consisting of a response header and body (https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#headers). It is important to note that once the HTTP response commences, the HTML or data being sent to the browser is already rendered in the response body. This implies that any server methods (such as setting a server cookie) would have executed before the HTTP response was constructed.

The crux of the matter lies in how React streaming functions: - simplified

Similar to progressive hydration, streaming is a rendering technique that enhances SSR performance by sending chunks of HTML from the node server to the client as they are created.

Hence, if a slow async sub-component attempts to set a cookie on the server during client-side streaming, it becomes impossible due to the ongoing stream like an AJAX request fetching HTML from the server.

For instance, when there are 20 slow components, React delivers the prerendered HTML along with the Suspense fallback HTML.

<FooBarComponent />
<Suspense fallback={<div>Loading...</div>}>
   <FreshlyLoadedContent />
</Suspense>

The HTTP response renders as follows:

<div>foo bar</div>
<div id="8:0">Loading...</div>

Any remaining streamed HTML is handled on the client side. React replaces sections of HTML (using an id="" for each streamed component) one by one in the DOM post the completion of slow asynchronous components' requests and renderings. Consequently, some parts of the page display a loading state while others are already rendered.

Once the streaming of a particular component finishes, it updates the div content as such:

<div>foo bar</div>
<div id="8:0">Fresly loaded content!</div>

The major advantage of this approach...

...is that browsers commence processing and displaying streamed content or partial responses earlier, resulting in reduced and consistent Time To First Byte (TTFB). The early parsing and rendering lead to swift First Paint (FP) and First Contentful Paint (FCP) owing to progressive rendering.

Consequently,

https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior - states:

Server actions can be triggered using the action attribute within a form element:

and

Server Actions are not restricted to forms and can be activated through event handlers, useEffect, third-party libraries, and other form elements like buttons.

This highlights that Server Actions specifically need to be initiated from client-side components. Think of submitting a form to the server or invoking a secure server-based API function to transmit sensitive Credit Card details. Does that make sense?

In this scenario, you must develop a client component and trigger your server action exampleAction(), potentially utilizing useEffect or another client-based action (e.g., onClick). This ensures the server performs an operation based on the client's action. This serves as a workaround.

You can also leverage middleware to both read and set a cookie prior to Next.js initiating the HTTP response back to the client and commencing any streaming:

import { NextRequest, NextResponse } from "next/server";

export function middleware(request){
  let response = NextResponse.next()
  response.cookies.set("foo", "bar")

  return response;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dealing with various node types in a parse tree using TypeScript: Tips and Tricks

I am in the process of converting my lexer and parser to TypeScript. You can find the current JavaScript-only code here. To simplify, I have created an example pseudocode: type X = { type: string } type A = X & { list: Array<A | B | C> } ty ...

What sets Next.js apart: Exploring the contrast between leveraging the "revalidate" option in getStaticProps versus utilizing the SWR package

One interesting feature of Next.js is the built-in "revalidate" option: export async function getStaticProps(context) { const data = await getData(); if (!data) { return { notFound: true, }; } return { props: { data }, reval ...

What is the process for utilizing datePipe in an Angular component?

How can I implement DatePipe in my Angular component? This is the code snippet that I am currently using. for (let i = 0; i < this.days.length; i++) { this.storeStart(this.days[i], null, null, null); } I have stored weekdays (Monday to Frid ...

Different outputs based on input in Typescript

Looking for some help with my class method. Here is the current implementation: handleTab<B extends boolean = false>(getIndex?: B): B extends true ? number : string { return getIndex ? 1 : ''; } Encountering an error message that s ...

Is there a way for Ionic to remember the last page for a few seconds before session expiry?

I have set the token for my application to expire after 30 minutes, and I have configured the 401/403 error handling as follows: // Handling 401 or 403 error async unauthorisedError() { const alert = await this.alertController.create({ header: 'Ses ...

Updating files in a Next.js project and automatically refreshing the page without the need to rerun 'npm run'

For a while now, I've been developing websites using a traditional LAMP stack with javascript (including jQuery) for the frontend. Recently, I decided to explore using javascript for the backend as well and started learning next.js. In the older meth ...

React Native: Once a user has successfully logged in, I would like the app to automatically direct them to the "Home" screen

After a user signs in, I would like my app to navigate home. However, it seems this is not working because the roots have not been updated. You can view the App code here to get a better understanding of what I am trying to communicate. What is the most e ...

Using Pocketbase OAuth in SvelteKit is not currently supported

I've experimented with various strategies, but I still couldn't make it work. Here's the recommendation from Pocketbase (): loginWithGoogle: async ({ locals }: { locals: App.Locals }) => { await locals.pb.collection('users' ...

What is the best way to transform a string into emojis using TypeScript or JavaScript?

Looking to convert emoji from string in typescript to display emoji in html. Here is a snippet of the Typescript file: export class Example { emoji:any; function(){ this.emoji = ":joy:" } } In an HTML file, I would like it to dis ...

When combining Jest async tests with mongodb-memory-server, an issue arises where the test consistently times out

I'm currently facing an issue with running my async jest tests properly while using the mongodb-memory-server package. In my code, I have a beforeAll function that connects to a memory mongo database: beforeAll(async () => await connect()); Follow ...

Guide to Angular Interface Styling - Ambiguous Suggestions

After reviewing the Angular style guide for interfaces, I find two recommendations particularly perplexing: The suggestion to use a class instead of an interface for services and declarables (components, directives, and pipes) leaves me puzzled. Similarl ...

Here's a method to extract dates from today to the next 15 days and exclude weekends -Saturday and Sunday

Is there a way to generate an array of dates starting from today and spanning the next 15 days, excluding Saturdays and Sundays? For example, if today is 4/5/22, the desired array would look like ['4/5/22', '5/5/22', '6/5/22' ...

Issue: formGroup requires an input of type FormGroup. Please provide one; Error: Unable to access property 'controls' as it is undefined

In the process of building a login and registration form using Angular with .Net core, I encountered an error upon running the program. The error is showing up in the Browser Console tab. This is my userlog.component.ts file: import { Component, OnInit, O ...

What is the best way to ensure that Interface (or type) Properties always begin with a particular character?

I attempted to tackle this assignment using template literals, but unfortunately, I wasn't successful. Here is the interface that I am working with: interface SomeInterface { '@prop1': string; '@prop2': string; '@ ...

"Enhance user experience with a real-time autocomplete feature in React that triggers an API call whenever

Currently, I am facing a problem while working with React and the autocomplete component of Material-UI. I need assistance in resolving this issue. In the examples of autocomplete that I have come across, it seems like you need to preload all the elements ...

Reference loss occurs in Angular Ionic when using @ViewChild

This situation is straightforward I am dealing with a reference in this format @ViewChild('myElement') myElementVar : SomeClass; The element I am referencing appears like this <element #myElement *ngIf="someBoolean"></element> As ...

Linux DigitalOcean VM experiencing termination of server process

I've been attempting to run a Next.js server on a DigitalOcean virtual machine. The server functions properly, but when I execute npm run start, the logs display Killed after approximately 1 minute. Here is an example log of the issue: joey@mydroplet ...

Tips for effectively implementing a type guard while iterating through nested arrays

When iterating through an array, it is necessary to distinguish between two potential types of elements: either each element is a string, or each element is an array of strings. function _addDataAtIndex( totalData: Array<string | string[]>, ...

Unable to include cookies in the request header on Mac OS X

Currently, I am attempting to include a cookie in the request header using the fetch function in React JS. The headers I am utilizing in the fetch call on the front end are as follows: Credentials: 'include' On the backend, I have configured C ...

Tips on how to specify a reference to a pre-existing namespace that can be accessed from the JavaScript bundle while it is

I am in the process of developing a plugin for an existing JavaScript application called Forge Autodesk.Viewing. Recently, they have integrated THREE.js into their app bundle starting from version 6. Currently, I am able to utilize it within my plugin by ...