DEV Community

Cover image for Writing A Custom Paragraph Component in React
John ✨️
John ✨️

Posted on • Updated on • Originally published at johna.hashnode.dev

Writing A Custom Paragraph Component in React

In this article, I'll be explaining how to create a custom paragraph component which will take a prop as well other valid HTML properties valid for a paragraph.

Let's say you were to write a custom paragraph component in React which would be called <ParaText />. You want to pass some string value as a property called text which would then be passed on to the p element like this:

<p>{text}</p>
Enter fullscreen mode Exit fullscreen mode

How would you go about writing this component? You may think about listing all valid properties for a paragraph element and then passing it on, but it soon becomes clear that this approach is unrealistic:

const ParaText = ({ text, id, class, style, title}) => <p>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

Here we've added just 4 attributes (and the text prop) that would need to be passed on. If we were to include all necessary attributes we would need 10 props and that's not a good approach. We could instead use the spread syntax to collect the remaining props in an object that we will call rest for this tutorial.

const ParaText = ({ text, ...rest}) => <p {...rest}>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

So far it's all been JavaScript but now we'll use an interface to define the type of the properties object so that only valid properties are passed to the ParaText component. We'll call the interface ParaTextType.

interface ParaTextType {
  text: string;
  id?: string;
  class?: string;
  style?: string;
  title?: string;
}

const ParaText = ({  text, ...rest}: ParaTextType) => 
<p {...rest}>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

But our ParaTexType definition for what a paragraph should accept isn't as precise as the built-in ones in React. And we only want to include the text property in our custom component and intrinsically inherit the normal attributes of the native paragraph element. There are a number of ways to achieve this but for this tutorial, we'll take a look at three:

1. ComponentPropsWithoutRef

This interface is available on the React namespace and it provides the appropriate props for a given component type without any ref that the component accepts. So that means if you passed a paragraph to ComponentPropsWithoutRef like this:

React.ComponentPropsWithoutRef<"p">
Enter fullscreen mode Exit fullscreen mode

You should get the valid props (attributes) for the paragraph element, which ParaTextType can extend. For brevity's sake, let's import ComponentPropsWithoutRef from React:


import { ComponentPropsWithoutRef } from 'react';

interface ParaTextType extends ComponentPropsWithoutRef<"p"> {
    text: string;
}

const ParaText = ({  text, ...rest}: ParaTextType) => <p {...rest}>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

Then you can simply use the component this way:

    <ParaText text="John" />
Enter fullscreen mode Exit fullscreen mode

Another interface similar to ComponentPropsWithoutRef is ComponentProps. The latter provides a ref property which can be used in the paragraph element.

2. JSX.IntrinsicElements

The namespace JSX is available globally from which we can extract the type we want by indexingJSX.IntrinsicElements with the HTML node name:

    JSX.IntrinsicElements['p']
Enter fullscreen mode Exit fullscreen mode

But we can't use extends with square brackets: this seems to be a TypeScript limitation. So we cannot do:

    interface ParaTexType extends JSX.IntrinsicElements['p']
Enter fullscreen mode Exit fullscreen mode

We would have to hold it in a separate type variable. Let's call this variable ParaTexTypeFromJSX, which we would then use like this:

type ParaTexTypeFromJSX = JSX.IntrinsicElements['p'];

interface ParaTexType extends ParaTexTypeFromJSX {
  label: string;
}

const ParaText = ({  text, ...rest}: ParaTextType) => <p {...rest}>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

3. HTMLParagraphElement

You can also define the return value of ParaText1 which in the end is a functional component that renders a paragraph. So we get the properties (HTML attributes) for a paragraph and use the intersection literal (&) to include our text prop.

const ParaText1: React.FunctionComponent<
  React.HTMLAttributes<HTMLParagraphElement> & {
    text: string;
  }
> = ({ text, ...rest }: { text: string }) => <p {...rest}>{text}</p>;
Enter fullscreen mode Exit fullscreen mode

Then you can then use the component as follows:

    <ParaText text="John" />
Enter fullscreen mode Exit fullscreen mode

And you should get a paragraph element displaying "John". You could extend this component as you wish, like limiting the text length and using an ellipsis (...) afterwards.

Conclusion

We looked at how we can create or mirror a paragraph element in React using the built-in types. There are other interfaces, like InputHTMLAttributes, HTMLAttributes and HTMLProps that might as well get the job done or be a bad choice but for this tutorial, we'll keep it at 3.

Top comments (0)