What is causing styled-components to include my attributes in the DOM?

In various parts of my code, there is a snippet like this:

import React from 'react'
import styled from 'styled-components'

type PropsType = {
  direction?: 'horizontal' | 'vertical'
  flex?: number | string
  width?: number | string
  minWidth?: number | string
  height?: number | string
  minHeight?: number | string
  fill?: string
  padding?: number | string
  paddingTop?: string | number
  paddingRight?: string | number
  paddingBottom?: string | number
  paddingLeft?: string | number
}

const Box = styled.div<PropsType>`
  display: flex;
  ${({ padding }) =>
    padding &&
    `padding: ${typeof padding === 'number' ? `${padding}px` : padding};`}
  ${({ width }) =>
    width && `width: ${typeof width === 'number' ? `${width}px` : width};`}
  ${({ minWidth }) =>
    minWidth &&
    `min-width: ${typeof minWidth === 'number' ? `${minWidth}px` : minWidth};`}
  ${({ height }) =>
    height && `height: ${typeof height === 'number' ? `${height}px` : height};`}
  ${({ minHeight }) =>
    minHeight &&
    `min-height: ${
      typeof minHeight === 'number' ? `${minHeight}px` : minHeight
    };`}
  ${({ flex }) => flex && `flex: ${flex};`}
  ${({ fill }) => fill && `background-color: ${fill};`}
  ${({ direction }) =>
    direction &&
    `flex-direction: ${direction === 'horizontal' ? 'row' : 'column'};`}
  ${({ paddingTop }) =>
    paddingTop &&
    `padding-top: ${
      typeof paddingTop === 'number' ? `${paddingTop}px` : paddingTop
    };`}
  ${({ paddingRight }) =>
    paddingRight &&
    `padding-right: ${
      typeof paddingRight === 'number' ? `${paddingRight}px` : paddingRight
    };`}
  ${({ paddingBottom }) =>
    paddingBottom &&
    `padding-bottom: ${
      typeof paddingBottom === 'number' ? `${paddingBottom}px` : paddingBottom
    };`}
  ${({ paddingLeft }) =>
    paddingLeft &&
    `padding-left: ${
      typeof paddingLeft === 'number' ? `${paddingLeft}px` : paddingLeft
    };`}
`

export default Box

Furthermore, I have the following component in another part of the code:

export default function Page() {

  return (
    <>
      <Box padding={24} width="100%">
        <h1>(tab)</h1>
      </Box>
      <Box fill={COLOR.white} paddingTop={24} paddingBottom={24}>
        (Text)
      </Box>
      <Box padding={24}>
        (Output)
      </Box>
    </>
  )
}

The issue that arises is with the fill attribute and other attributes displaying in the DOM:

https://i.stack.imgur.com/6kXrN.png

Is this behavior normal? If not, how can I remove it from the DOM?

My project utilizes Next.js and has the default configuration in next.config.js, along with this tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "types": ["node"],
    "baseUrl": "."
  },
  "include": ["next-env.d.ts", "index.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"],
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs"
    }
  }
}

Answer №1

Typically, we anticipate that styled-components will not pass additional props to styled base HTML tags, such as div, as it is considered "smart enough" to filter them out automatically:

When the styled target is a simple element (e.g. styled.div), [...], styled-components automatically filters non-standard attributes for you.

However, in certain cases like this one, the term "smart enough" falls short: the fill attribute, which is standard for SVG elements, gets passed through by styled-components.


An easy workaround involves using a transient prop instead. This entails prefixing the prop with a dollar sign:

To prevent props intended for use by styled components from reaching the underlying React node or being rendered to the DOM element, prepend the prop name with a dollar sign ($) to make it transient.

const Box2 = styled.div<{ $fill?: string; }>`
  ${({ $fill }) => $fill && `background-color: ${$fill};`}
`;

<Box2 $fill="yellow">Content...</Box2>

Unfortunately, this does require a slight alteration to your internal API.


Alternatively, another solution involves utilizing the shouldForwardProp configuration. With this approach, you can precisely define what should be forwarded or withheld:

A prop that fails the test won't be passed down to underlying components, similar to a transient prop.

const Box3 = styled.div.withConfig({
  shouldForwardProp(prop, isValidProp) {
    return (prop as string) !== "fill" && isValidProp(prop);
  }
})<{ fill?: string; }>`
  ${({ fill }) => fill && `background-color: ${fill};`}
`;

<Box3 fill="yellow">Content...</Box3>

Demo: https://codesandbox.io/s/wispy-silence-mp54zv?file=/src/App.tsx


Further clarification on the built-in filtering mechanism of styled-components: when a prop represents a standard attribute for any HTML element (like fill for SVG), it is allowed through due to reliance on @emotion/is-prop-valid. This implementation utilizes a single common whitelist for simplicity, rather than maintaining individual lists per HTML tag.

The white listing of the fill attribute can be observed here.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring Virtual Reality with the Oculus Quest2 and Three.js

Currently, I am working on a project using Oculus and three.js to create a virtual reality experience. To test the functionality, I decided to try out the official sample provided. Here is the link to the official sample. My intention was to access the s ...

Tips for personalizing the Material-UI sticky header to work with horizontal scrolling

Check out this example of a Material UI table with a sticky header on CodeSandox: https://codesandbox.io/s/yi98d?file=/demo.tsx I encountered an issue where the left side header cells slide behind each other when I added a stickyHeader prop to the Materia ...

Why is it that when I store a DOM element reference in a JavaScript Array, I cannot reuse that stored reference to add an event listener

I have a little confusion and I hope someone can help me out. I am facing an issue with dynamically created buttons, where each button has a unique id. To keep track of these buttons in a well-organized manner, I store their references using a simple two-d ...

nodemon failing to automatically refresh files in typescript projects

I am currently developing an app using NodeJs, express, typescript, and nodemon. However, I am encountering an issue where the page does not refresh when I make changes to the ts files. Is there a way for me to automatically compile the ts files to js an ...

Utilizing $resource within a promise sequence to correct the deferred anti-pattern

One challenge I encountered was that when making multiple nearly simultaneous calls to a service method that retrieves a list of project types using $resource, each call generated a new request instead of utilizing the same response/promise/data. After doi ...

Overloading TypeScript functions with Observable<T | T[]>

Looking for some guidance from the experts: Is there a way to simplify the function overload in the example below by removing as Observable<string[]> and using T and T[] instead? Here's a basic example to illustrate: import { Observable } from ...

Error occurred during module build: SyntaxError: An unexpected token was found, a comma was expected

Although I have thoroughly checked for any errors, an error appeared in the terminal. Take a look at the following image for reference: view image details here ...

Disabling ngIf but still utilizing ngContent will render the template bindings

Creating a basic component in the following way: @Component({ selector: 'loader', template: `<div *ngIf='false'> <ng-content></ng-content> </div>`, }) export class Loader {} When it is imple ...

Why am I encountering the 'nonexistent type' error in my Vue 3 project that uses Typescript and Vuelidate?

Seeking assistance with a Vue 3 and Vuelidate issue. I followed the configuration guide provided at . <script lang="ts"> import { required, minLength, maxLength, numeric } from '@vuelidate/validators' import useVuelidate from &apo ...

Is there a way to apply styles to a checkbox's child element depending on the checkbox's state without relying on ternary operators within Styled Components?

I am currently working on styling this component using Styled Components, but I feel like my current approach is a bit of a hack. What would be the best practice for incorporating Styled Components to style this component effectively? If I were to use Pla ...

Synchronize Docker volumes

Hey there! I've been trying to dive into learning Docker, but I'm having trouble syncing the host and container using volumes when making changes and saving code (specifically using npm run dev). Every time I need to restart docker-compose up --b ...

Achieving dynamic HTML element addition through JavaScript while maintaining their record in PHP

There is an input box where the user specifies the number of new DIV elements to be added dynamically. This request is then sent to PHP via Ajax, where it prepares an HTML DIV with some inner input tags. Each DIV has a unique id attribute generated increme ...

Undefined Children Component

I am currently working on creating Auth routes and I am facing an issue where the children are undefined, resulting in a blank page. In my App.js file, I have implemented a PrivateRoute component as shown below. Interestingly, when I replace PrivateRoute w ...

Tips for managing Vue component content prior to data being fully loaded

I'm currently integrating product category data from Prismic into my Nuxt project and seeking guidance on best practices. Specifically, I need clarity on managing the state when data is still being fetched and there's no content to display in the ...

Store the current user's authentication information from Firebase in the Redux state using React-Redux

I am facing an issue with persisting the state of my redux store using the currentUser information from Firebase Auth. The problem arises when I try to access auth.currentUser and receive null, which I suspect is due to the asynchronous loading of the curr ...

Update the status of another component when clicked utilizing Material-UI within a React application

I want to implement a feature where the number displayed on a badge component resets every time it is clicked. The number currently comes from an array of objects called dummyData. It would be ideal to reset this number upon each click event. I am utiliz ...

Ways to retrieve data from response instead of subscription JSON in Angular 2/4

My Service : retrieveData(url,request) { return this.http.post(this.apiUrl+url,request).subscribe( (response) => {return response.json()} ); } My Component : ngOnInit() { this.data = this.dataService.retrieveData(&apos ...

"Enhance your Material-UI ListItems with dynamic ripple colors when using React-Router NavLink

Currently, I am attempting to nest a Material-UI <ListItem button> within a react-router <NavLink>. While functionality is present, I have observed that the ripple colors on the <ListItem button> are being altered by the <NavLink> C ...

Choose the text that appears in the input or textbox field when tapping or clicking on it

Desperately Seeking a Clickable Textbox The Quest: In search of a cross-browser textbox/input field that can select its content on click or tap. An elusive challenge haunting developers for years. The Dilemma: Using a touch device triggers the tap e ...

Next.js app experiencing issues with accessing Azure App environment variables stored in Application Settings

I have encountered an issue while trying to deploy my Next.js application to Azure. It seems that the application is not reading the environment variables set in the Application Settings. For instance, I have defined a variable named "NEXT_PUBLIC_AZURE_ENV ...