Ask
158
@yaml_yuri ·

Importing my db helper into a component broke the build with a next/headers error, where is the boundary supposed to sit

I have a components folder where roughly half the files start with 'use client' because they use state, and a getSession helper that reads cookies. Importing that helper into one of those components gives a build error about next/headers not being allowed. I can make the error go away by moving things around semi randomly and I would rather understand the rule than keep guessing.

8 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @till_and_tally · 3mo ago · 3 replies

    The mental model that fixes this permanently is that 'use client' is not a label on one file, it is a door into the client bundle. Everything that file imports, and everything those files import, gets pulled through the door with it, which is why a helper that reads cookies suddenly finds itself being compiled for the browser and complains. The boundary is therefore not where you wrote the directive, it is the entire subtree below it. Once you see it as a subtree rather than a file, the placement rules stop feeling arbitrary.

    231
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @hemline_hank · 3mo ago

      Add the server only package to that helper and the error becomes explicit at build time instead of a confusing complaint about headers.

      59
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
    • @yaml_yuri · 3mo ago

      Subtree rather than file is the bit I was missing. That explains why moving an import fixed it in a way that seemed like magic.

      68
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @couchto5kagain · 3mo ago · 2 replies

    Practical rule that has survived two large apps: read data on the server, pass plain data down as props, and push 'use client' as far towards the leaves as it will go. If a component needs interactivity in the middle of an otherwise server rendered tree, make the interactive part its own small client component and pass server rendered content into it as children, because children passed through are rendered on the server and do not get dragged into the bundle. That single trick removes most of the situations where people feel forced to mark a whole page as client.

    196
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
    • @yaml_yuri · 4mo ago

      Passing server rendered content in as children is the escape hatch I did not know existed. That solves the three components I was about to duplicate.

      62
      Share
      Reply

      Answering anonymously — a moderator will review it first.

      Report
  • @deadbug_wiring · 3mo ago

    Anything importing next/headers or your database is server only, mark it as such and let the build tell you. Data down as props, directive at the leaves, children as the escape hatch.

    121
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @layer_shift_lu · 3mo ago

    I put 'use client' at the top of the root layout during a bad week because it made twenty errors disappear at once. Everything worked, and then the bundle roughly doubled, the first load got noticeably slower, and I had thrown away the entire reason for using this architecture. Unpicking it took longer than fixing the original twenty errors would have. Do not put the directive high up to silence errors, it silences the framework too.

    172
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @burr_check_bea · 2h ago

    Mild dissent on leaf purity. For genuinely form heavy internal tools, I have had a better time treating whole pages as client components and doing every data mutation and read through actions, because the mental overhead of tracking which half of a component tree is which was costing my team more than the bundle size was costing our users. It is the wrong default for a public marketing site and the right one for an admin panel behind a login. Pick per surface rather than per framework opinion.

    1
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report