Skip to content

Instantly share code, notes, and snippets.

@domwebber
Last active February 15, 2023 10:21
Show Gist options
  • Save domwebber/c3d3e747df505e2b02c70a7dd6cb8306 to your computer and use it in GitHub Desktop.
Save domwebber/c3d3e747df505e2b02c70a7dd6cb8306 to your computer and use it in GitHub Desktop.
TypeScript React "as" Typing

TypeScript React "as" Typing

Example implementation for an "as" property for a react component.

This gist includes two options for the base Container component - one using classnames and one using clsx.

Note: This example uses TailwindCSS classes for an example of specialising styles.

See my Medium Article discussing this code's development and for related articles and reading on TypeScript generics with React.

const children = "Example";

<Container>{children}</Container> // Default <div> tag
<Container as="article">{children}</Container> // Using an <article> tag

<PaddedContainer>{children}</PaddedContainer> // Default <div> tag with slight padding
<PaddedContainer as="article">{children}</PaddedContainer> // Using an <article> tag with slight padding

<StandardContainter>{children}</StandardContainer> // Default <div> tag with slight padding and margin
<StandardContainer as="article">{children}</StandardContainer> // Using an <article> tag with slight padding and margin
// clsx variant of the base Container component
import React, { ReactNode, ElementType, ComponentPropsWithoutRef } from "react";
import clsx, { ClassValue } from "clsx";
export interface ContainerProps<Tag extends ElementType> {
children: ReactNode;
className?: ClassValue;
as?: Tag;
}
/**
* @see https://stackoverflow.com/a/66568474/11928183
*/
type ContainerAdditionalProps<Tag extends ElementType> = Omit<
ComponentPropsWithoutRef<Tag>,
keyof ContainerProps<Tag>
>;
export default function Container<Tag extends ElementType = "div">({
children,
className,
as,
...props
}: ContainerProps<Tag> & ContainerAdditionalProps<Tag>) {
const Component = as || "div";
return (
<Component
className={clsx("md:container", "md:mx-auto", className)}
{...props}
>
{children}
</Component>
)
}
import { ReactNode, ElementType, ComponentPropsWithoutRef } from "react";
import classNames, { Argument } from "classnames";
/**
* @see https://stackoverflow.com/a/66568474/11928183
*/
export interface ContainerProps<Tag extends ElementType> {
children: ReactNode;
className?: Argument;
as?: Tag;
}
/**
* @see https://stackoverflow.com/a/66568474/11928183
*/
type ContainerAdditionalProps<Tag extends ElementType> = Omit<
ComponentPropsWithoutRef<Tag>,
keyof ContainerProps<Tag>
>;
export default function Container<Tag extends ElementType = "div">({
children,
className,
as,
...props
}: ContainerProps<Tag> & ContainerAdditionalProps<Tag>) {
const Component = as || "div";
return (
<Component
className={classNames("md:container", "md:mx-auto", className)}
{...props}
>
{children}
</Component>
);
}
import React, { ComponentPropsWithoutRef, ElementType } from "react";
import Container, { ContainerProps } from "./Container";
export interface PaddedContainerProps<Tag extends ElementType>
extends ContainerProps<Tag> {}
/**
* @see https://stackoverflow.com/a/66568474/11928183
*/
type PaddedContainerAdditionalProps<Tag extends ElementType> = Omit<
ComponentPropsWithoutRef<Tag>,
keyof PaddedContainerProps<Tag>
>;
export default function PaddedContainer<Tag extends ElementType = "div">({
children,
className,
...props
}: PaddedContainerProps<Tag> & PaddedContainerAdditionalProps<Tag>) {
return (
<Container className={["px-4", className]} {...props}>
{children}
</Container>
);
}
import React, { ComponentPropsWithoutRef, ElementType } from "react";
import PaddedContainer, { PaddedContainerProps } from "./PaddedContainer";
export interface StandardContainerProps<Tag extends ElementType>
extends PaddedContainerProps<Tag> {}
/**
* @see https://stackoverflow.com/a/66568474/11928183
*/
type StandardContainerAdditionalProps<Tag extends ElementType> = Omit<
ComponentPropsWithoutRef<Tag>,
keyof StandardContainerProps<Tag>
>;
export default function StandardContainer<Tag extends ElementType = "div">({
children,
className,
...props
}: StandardContainerProps<Tag> & StandardContainerAdditionalProps<Tag>) {
return (
<PaddedContainer className={["mb-8", className]} {...props}>
{children}
</PaddedContainer>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment