Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how can use it with next 13? #44

Closed
sinafath opened this issue Jul 29, 2023 · 7 comments
Closed

how can use it with next 13? #44

sinafath opened this issue Jul 29, 2023 · 7 comments

Comments

@sinafath
Copy link

sinafath commented Jul 29, 2023

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.

@mfaibrahem
Copy link

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

@sinafath sinafath changed the title how can sue it with next 13? how can use it with next 13? Aug 9, 2023
@chemicstry
Copy link

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.

@theo-lubert
Copy link

theo-lubert commented Aug 29, 2023

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>
    )
}
}

@chemicstry
Copy link

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.

@theo-lubert
Copy link

theo-lubert commented Aug 30, 2023

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

@AlessioPorcasi
Copy link

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"

@andreizanik
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants