andreizanik / cookies-next

Getting, setting and removing cookies on both client and server with next.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

how can use it with next 13?

sinafath opened this issue · comments

is there any documentation to use cookies-next with new version of next.js (app directory). I think this library is still relevant when nextjs official cookies api doesn't yet support client side and the api is server only.

Did you find a solution to work with next 13 !?

https://gist.github.com/chemicstry/6a8e15d11517c9c7685a5ea72af2e8f8

This works from both server-side and client-side. It is implemented as react state, but you can extract just the cookie setting functions.

While @chemicstry solution could work: you could wrap the two behaviors (server vs browser) into a single function.

const isSsr = (): boolean => typeof window === 'undefined';
const ssrCookies = isSsr() ? require("next/headers").cookies : undefined;

You probably should not, as it is discouraged and considered bad practice.
The reason is that the server code will be included in the client js bundle (tree-shaking won't work here).

It might be sort-of OK to import next/headers, I haven't looked at what actually ends up in the js bundle. But I would not recommend it, consider this:

It may cause unexpected issues, secret leaks, etc.
Here, you have no control over the library size, or dependencies. If for example, the library were to import fs, you client would not compile, or maybe even crash at runtime in some cases... the debugging experience might get ugly pretty quickly from there.
It could also add several MB of unused javascript shipped to every clients...

And even more dangerous: importing your own code might easily leak sensitive data (secrets keys) if you're not carefull. Importing a file that contains an API call with a secret token, will most likely end up in the public js bundle...

It is kinda being discussed currently, but it's not very active, and it is not likely to be resolved any time soon IMO. See:


The "supported" approach would be to split your component in two, and provide the SSR cookie as a prop to the Client Component, like so:

ServerComponent.tsx

import { cookies } from 'next/headers'

export default function ServerComponent() {
    const cookie = cookies().get('myCookie')?.value
    return (
        <ClientComponent initial={{ cookie }} />
    )
}

ClientComponent.tsx

'use client'

import { getCookie } from 'cookies-next'

export default function ClientComponent(props: { inital: { cookie?: string }}) {
    const cookie = getCookie('myCookie')?.toString()
    return (
        <p>{cookie ?? props.initial.cookie}</p>
    )
}
}

Wow, this whole SSR thing is really fragile. Thanks for in depth explanation.

Do you know if it's possible to make it work with plain functions instead of react components? To implement useCookieState() like in my example.

Unfortunatly, I don't think so.

Even in a plain function, in a separate file, you're most likely going to import it in a component somehow, and that's what's important here.

You may still choose to do it, with the downsides I mentioned, at your own risk
But currently, it comes down to tricking the compiler (which won't let you do that, so it's not that fragile)

Server Components are still pretty new, it migth get more attention in the near futur.
But arguably, the compiler is right to prevent this right now. As long as it is not standard for our tools to remove code under typeof window === 'undefined', the compiler is actually doing a good job at keeping you safe. It's a massive pain point though... 😢

Minifying, tree-shaking, etc, was exclusively used for Client code, where this condition did not make sense... until now


Edit:

One possible workaround that may be a bit advanced (the compiler doesn't help, you are on your own), so I still woudn't recommend it:
Webpack allows you to mark arbitrary files, so they are pruned. Other tools migth have similar options.
https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free

While @chemicstry solution could work: you could wrap the two behaviors (server vs browser) into a single function.

const isSsr = (): boolean => typeof window === 'undefined';
const ssrCookies = isSsr() ? require("next/headers").cookies : undefined;

You probably should not, as it is discouraged and considered bad practice. The reason is that the server code will be included in the client js bundle (tree-shaking won't work here).

It might be sort-of OK to import next/headers, I haven't looked at what actually ends up in the js bundle. But I would not recommend it, consider this:

It may cause unexpected issues, secret leaks, etc. Here, you have no control over the library size, or dependencies. If for example, the library were to import fs, you client would not compile, or maybe even crash at runtime in some cases... the debugging experience might get ugly pretty quickly from there. It could also add several MB of unused javascript shipped to every clients...

And even more dangerous: importing your own code might easily leak sensitive data (secrets keys) if you're not carefull. Importing a file that contains an API call with a secret token, will most likely end up in the public js bundle...

It is kinda being discussed currently, but it's not very active, and it is not likely to be resolved any time soon IMO. See:

The "supported" approach would be to split your component in two, and provide the SSR cookie as a prop to the Client Component, like so:

ServerComponent.tsx

import { cookies } from 'next/headers'

export default function ServerComponent() {
    const cookie = cookies().get('myCookie')?.value
    return (
        <ClientComponent initial={{ cookie }} />
    )
}

ClientComponent.tsx

'use client'

import { getCookie } from 'cookies-next'

export default function ClientComponent(props: { inital: { cookie?: string }}) {
    const cookie = getCookie('myCookie')?.toString()
    return (
        <p>{cookie ?? props.initial.cookie}</p>
    )
}
}

While @chemicstry solution could work: you could wrap the two behaviors (server vs browser) into a single function.

const isSsr = (): boolean => typeof window === 'undefined';
const ssrCookies = isSsr() ? require("next/headers").cookies : undefined;

You probably should not, as it is discouraged and considered bad practice. The reason is that the server code will be included in the client js bundle (tree-shaking won't work here).

It might be sort-of OK to import next/headers, I haven't looked at what actually ends up in the js bundle. But I would not recommend it, consider this:

It may cause unexpected issues, secret leaks, etc. Here, you have no control over the library size, or dependencies. If for example, the library were to import fs, you client would not compile, or maybe even crash at runtime in some cases... the debugging experience might get ugly pretty quickly from there. It could also add several MB of unused javascript shipped to every clients...

And even more dangerous: importing your own code might easily leak sensitive data (secrets keys) if you're not carefull. Importing a file that contains an API call with a secret token, will most likely end up in the public js bundle...

It is kinda being discussed currently, but it's not very active, and it is not likely to be resolved any time soon IMO. See:

The "supported" approach would be to split your component in two, and provide the SSR cookie as a prop to the Client Component, like so:

ServerComponent.tsx

import { cookies } from 'next/headers'

export default function ServerComponent() {
    const cookie = cookies().get('myCookie')?.value
    return (
        <ClientComponent initial={{ cookie }} />
    )
}

ClientComponent.tsx

'use client'

import { getCookie } from 'cookies-next'

export default function ClientComponent(props: { inital: { cookie?: string }}) {
    const cookie = getCookie('myCookie')?.toString()
    return (
        <p>{cookie ?? props.initial.cookie}</p>
    )
}
}

Finally! I had been looking for this solution for weeks. It was enough to separate the "server" from the "client"

Hi all!
Good news. Our library began supporting next.js v13+.
If this is relevant for you, update to v4.1.0