remarkjs / react-remark

React component and hook to use remark to render markdown
https://remarkjs.github.io/react-remark
MIT License
208 stars 7 forks source link

Typing for custom components #6

Closed tremby closed 4 years ago

tremby commented 4 years ago

I'm trying to add some custom components for rendering certain elements.

I'm getting a Typescript error which I believe to be a typing error on the react-remark side, though maybe it's something I'm doing wrong.

lib/markdown.tsx:37:11 - error TS2322: Type 'FC<ArbitraryLinkProps>' is not assignable to type 'ComponentLike<ReactElement<{}, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)>>'.
  Types of parameters 'props' and 'props' are incompatible.
    Type 'ComponentProps' has no properties in common with type 'PropsWithChildren<ArbitraryLinkProps>'.

Should I not be using React.FC in this case?

ChristianMurphy commented 4 years ago

That typing is coming from https://github.com/rehypejs/rehype-react it should be compatible with FC https://github.com/rehypejs/rehype-react/blob/414e4becae174fc3edcdfb458a45a4ae878e7f46/types/rehype-react-test.tsx#L32-L57

Could you share the source of the component and how you attached it to react-remark?

tremby commented 4 years ago

My ArbitraryLink component is like this:

import Link from "next/link";
import isLocal from "../utils/is-local"; // This is (string) => boolean, says if a link is local or not
export type ArbitraryLinkProps = React.HTMLProps<HTMLAnchorElement>;
export const ArbitraryLink: React.FC<ArbitraryLinkProps> = ({ href, ...props }) => {
  if (href != null && isLocal(href)) {
    return (
      <Link href={href}>
        <a {...props} />
      </Link>
    );
  }
  return <a href={href} {...props} />;
};

I attached it like this:


<Remark
  rehypeReactOptions={{
    components: {
      a: ArbitraryLink,
    },
  }}
>
  {markdownSource}
</Remark>
ChristianMurphy commented 4 years ago

A component being passed to rehype-react needs to include a node property https://github.com/rehypejs/rehype-react/blob/414e4becae174fc3edcdfb458a45a4ae878e7f46/types/index.d.ts#L10-L15 it requires node to be on the typing, to prevent a conflicting node typing which could cause a crash with the current typing,

export type ArbitraryLinkProps = React.HTMLProps<HTMLAnchorElement> & { node?: unist.Node};

should work. If you want to offer an alternative typing, a PR to https://github.com/rehypejs/rehype-react could be an option

tremby commented 4 years ago

Should that appear in the example custom component in the readme, then?

ChristianMurphy commented 4 years ago

The example in the readme is valid typescript https://codesandbox.io/s/tender-fog-rd8gf?file=/src/App.tsx and is the way I would personally recommend typing elements. I also want to avoid overloading the readme with too many specific edge cases.

If you want to add it, a PR giving an example of a custom component in typescript in the storybook stories would be welcome.

tremby commented 4 years ago

I guess we have different settings; I get warnings about implicit "any" and missing display name. Thanks for your help.

ChristianMurphy commented 4 years ago

:raised_eyebrow: Maybe? I have strict mode and no implicit any on and don't see that error https://codesandbox.io/s/elegant-jones-pg8u9

tremby commented 4 years ago

You get the "implied any" if you take it out of the context of that options object, which you'd want to do (or I would at least) if it's anything more than a couple of lines long.

https://codesandbox.io/s/hardcore-hellman-ws9ic

ChristianMurphy commented 4 years ago

Gotcha, that makes sense. A PR adding an example of this to storybook would be welcome!

imclint21 commented 7 months ago
components: {
      "documentation-page": DocumentationPage,
      "info-box": InfoBox,
      "copyright-notice": CopyrightNotice,
    },

I'm interested by doing something like this @ChristianMurphy, any possibility? maybe I could use https://github.com/marekweb/rehype-components

ChristianMurphy commented 7 months ago

@imclint21 your question is unrelated to the above, you are adding new intrinsic elements. react-remark, rehype-react, react-markdown, and mdx all support this through extensions of the React intrinsic. You can find more on that here: https://goulet.dev/posts/consuming-web-component-react-typescript/

If you have follow up questions please open a discussion with more context https://github.com/orgs/remarkjs/discussions/new?category=q-a