Search code examples
htmlreactjsinputstyled-components

How to customized an input component from another component?


For this input component, I want to make it as a basic component for other components.

import React, { InputHTMLAttributes } from "react";
import styled, { css } from "styled-components";

export const TextField: React.FC<InputHTMLAttributes<HTMLInputElement>> = (
  props
) => {
  return (
    <Input type="text" {...props} />
  );
};

const Input = styled.input`
  ${({ theme }) => css`
    width: 100%;
    color: ${theme.color.text};
  `}
`;

This is a customized one and add some styles:

import React from "react";
import styled, { css } from "styled-components";
import { TextField } from "~/components";

export type ProductFieldProps = {
  name: string;
  value?: string;
};

type ProductFieldTypeProps = {
  textAlign: string;
};

export const ProductField: React.FC<ProductFieldProps> = ({
  name,
  value,
}) => {
  return (
    <StyledTextField textAlign="center">
      <TextField name={name} value={value} />
    </StyledTextField>
  );
};

const StyledTextField = styled(TextField)`
  ${({ theme }) => css<ProductFieldTypeProps>`
    &:hover {
      border-color: ${theme.color.hover};
    }
  `}
`;

When use the new component ProductField,

# pages/index.tsx
import { ProductField } from "~/components";

...
return {
  <>
    <ProductField name="aaa" value="bbb" />
  </>
}

I got a server error:

Error: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML.

How to set correctly?


Solution

  • You are using styled-components to style your Input component.

    You can see on this line that you are applying styles to the TextField component:

    const StyledTextField = styled(TextField)
    

    And yet in the new ProductField component you import both, the styled version and the original one and nest them in each other:

        <StyledTextField textAlign="center">
          <TextField name={name} value={value} />
        </StyledTextField>
    

    This essentially means you are nesting two input field into each other, which is not allowed and hence the error:

    Error: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML.

    One way to approach this is to simply remove the nested TextField like so, and apply all the props to the styled one instead:

    <StyledTextField textAlign="center" name={name} value={value} />
    

    Another solution would be to just export and use the styled version directly because the additional wrapper seems redundant.

    More info on how to style custom components with styled-components