Writing A Custom Paragraph Component in React
Tags: react, webdev, javascript, tutorial
Published: 2023-03-19 22:50:40

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:

```JSX
<p>{text}</p>
```

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:

```TypeScript
const ParaText = ({ text, id, class, style, title}) => <p>{text}</p>;
```

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.

```TypeScript
const ParaText = ({ text, ...rest}) => <p {...rest}>{text}</p>;
```

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`.

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

const ParaText = ({  text, ...rest}: ParaTextType) => 
<p {...rest}>{text}</p>;
```

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:

```typescript
React.ComponentPropsWithoutRef<"p">
```

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:

```typescript

import { ComponentPropsWithoutRef } from 'react';

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

const ParaText = ({  text, ...rest}: ParaTextType) => <p {...rest}>{text}</p>;
```

Then you can simply use the component this way:

```javascript
	<ParaText text="John" />
```

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 indexing`JSX.IntrinsicElements` with the HTML node name:

```typescript
	JSX.IntrinsicElements['p']
```

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

```TypeScript
    interface ParaTexType extends JSX.IntrinsicElements['p']
```

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

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

interface ParaTexType extends ParaTexTypeFromJSX {
  label: string;
}

const ParaText = ({  text, ...rest}: ParaTextType) => <p {...rest}>{text}</p>;
```

### 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.

```typescript
const ParaText1: React.FunctionComponent<
  React.HTMLAttributes<HTMLParagraphElement> & {
    text: string;
  }
> = ({ text, ...rest }: { text: string }) => <p {...rest}>{text}</p>;
```

Then you can then use the component as follows:

```javascript
	<ParaText text="John" />
```

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.