linaria icon indicating copy to clipboard operation
linaria copied to clipboard

Styling a component with forwardRef

Open molily opened this issue 1 year ago • 4 comments

Environment

  • Linaria version: @linaria/core 6.2.0, @linaria/react 6.2.1
  • Next.js 14.2.5, next-with-linaria 0.7.0
  • Node.js version: 20.16.0
  • OS: macOS 14.5

Description

Hi, I'm using Linaria in a Next project via next-with-linaria.

In a file marked with 'use client', I have a component defined with forwardRef that accepts styles:

const StylableInputFwdRef = forwardRef<HTMLInputElement, StylableInputProps>(({ label, className, style }, ref) =>
  <p>
    <label>
      {label}{" "}
      <input ref={ref} className={ className } style={ style } />
    </label>
  </p>
);

I would like to style this component with Linaria:

const InputLinariaFwdRef = styled(StylableInputFwdRef)`
  border: 2px inset blue;
`;

This declaration raises an error:

next14-linaria/node_modules/@wyw-in-js/transform/lib/module.js:224
      throw new EvalError(`${e.message} in${this.callstack.join('\n| ')}\n`);
      ^

EvalError: TextEncoder is not defined in next14-linaria/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js
| next14-linaria/node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js
| next14-linaria/node_modules/next/dist/server/future/route-modules/app-page/vendored/rsc/react.js
| next14-linaria/src/app/ClientComponent.tsx
    at evalFile.next (<anonymous>)

The error seems to happen in Next.js code but is re-thrown by wyw-in-js.

I'm not sure whether my problem is caused by Linaria itself, by next-with-linaria, wyw-in-js or Next.js. I'd appreciate any hints.

Is is possible to wrap a component with forwardRef? Am I fundamentally misunderstanding how Linaria works in a client component?

Thanks for your time and thank you for this project!

Reproducible Demo

Fresh Next.js project with Linaria: https://github.com/molily/next14-linaria Relevant code is in https://github.com/molily/next14-linaria/blob/main/src/app/ClientComponent.tsx

  • npm install
  • npm run dev
  • Go to http://localhost:3000

molily avatar Aug 05 '24 15:08 molily

Hi,

the issue seems to be a typing issue of forwardRef. Assigning the return-type of forwardRef to StylableInputFwdRef works:

- const StylableInputFwdRef = forwardRef<HTMLInputElement, StylableInputProps>(({ label, className, style }, ref) =>
+ const StylableInputFwdRef: React.ForwardRefExoticComponent<React.PropsWithoutRef<StylableInputProps> & React.RefAttributes<HTMLInputElement>> = forwardRef(({ label, className, style }, ref) =>

Note that linaria's styled already calls forwardRef on every component regardless whether it's necessary or not.

espretto avatar Mar 25 '25 10:03 espretto

Any updates on this? I tried the suggested solution, but it didn’t work.

EdiAfremovFactify avatar May 22 '25 10:05 EdiAfremovFactify

Hi,

the issue seems to be a typing issue of forwardRef. Assigning the return-type of forwardRef to StylableInputFwdRef works:

  • const StylableInputFwdRef = forwardRef<HTMLInputElement, StylableInputProps>(({ label, className, style }, ref) =>
  • const StylableInputFwdRef: React.ForwardRefExoticComponent<React.PropsWithoutRef<StylableInputProps> & React.RefAttributes<HTMLInputElement>> = forwardRef(({ label, className, style }, ref) => Note that linaria's styled already calls forwardRef on every component regardless whether it's necessary or not.

Thanks for the answer. I had the same issue, and your solution worked for me. Could you please explain why assigning the return type is actually needed? Is there any other workaround?

EdiAfremovFactify avatar Jun 25 '25 19:06 EdiAfremovFactify

@EdiAfremovFactify linaria's styled never returns a ForwardRefExoticComponent no matter the type of the component passed as its first argument. It is just missing a type signature handling that special case.

If this solved your issue, too, @molily it'd be great if you could close the issue.

espretto avatar Nov 19 '25 15:11 espretto