diff --git a/packages/mui-system/src/createStyled.js b/packages/mui-system/src/createStyled.js
index ee5c9fb06a6ebc..f4c86e0a7755bf 100644
--- a/packages/mui-system/src/createStyled.js
+++ b/packages/mui-system/src/createStyled.js
@@ -8,6 +8,17 @@ function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
+// https://github.com/emotion-js/emotion/blob/26ded6109fcd8ca9875cc2ce4564fee678a3f3c5/packages/styled/src/utils.js#L40
+function isStringTag(tag) {
+ return (
+ typeof tag === 'string' &&
+ // 96 is one less than the char code
+ // for "a" so this is checking that
+ // it's a lowercase character
+ tag.charCodeAt(0) > 96
+ );
+}
+
const getStyleOverrides = (name, theme) => {
if (theme.components && theme.components[name] && theme.components[name].styleOverrides) {
return theme.components[name].styleOverrides;
@@ -104,6 +115,9 @@ export default function createStyled(input = {}) {
} else if (componentSlot) {
// any other slot specified
shouldForwardPropOption = slotShouldForwardProp;
+ } else if (isStringTag(tag)) {
+ // for string (html) tag, preserve the behavior in emotion & styled-components.
+ shouldForwardPropOption = undefined;
}
const defaultStyledResolver = styledEngineStyled(tag, {
diff --git a/packages/mui-system/src/createStyled.test.js b/packages/mui-system/src/createStyled.test.js
index 39be9742a592f6..a664ad3208556b 100644
--- a/packages/mui-system/src/createStyled.test.js
+++ b/packages/mui-system/src/createStyled.test.js
@@ -281,4 +281,57 @@ describe('createStyled', () => {
});
});
});
+
+ it('does not spread `sx` prop to DOM', () => {
+ const styled = createStyled({});
+ const Button = styled('button')({});
+
+ const { container } = render();
+ expect(container.firstChild).not.to.have.attribute('sx');
+ });
+
+ it('does not forward `ownerState` prop to DOM', () => {
+ const styled = createStyled({});
+ const Button = styled('button')({});
+
+ const { container } = render();
+ expect(container.firstChild).not.to.have.attribute('ownerState');
+ });
+
+ describe('default behaviors', () => {
+ it('does not forward invalid props to DOM if no `slot` specified', () => {
+ // This scenario is usually used by library consumers
+ const styled = createStyled({});
+ const Button = styled('button')({});
+
+ const { container } = render(
+ ,
+ );
+ expect(container.firstChild.getAttribute('data-foo')).to.equal('bar');
+ expect(container.firstChild.getAttribute('color')).to.equal('red'); // color is for Safari mask-icon link
+ expect(container.firstChild.getAttribute('shouldBeRemoved')).not.to.equal('true');
+ });
+
+ it('can use `as` prop', () => {
+ const styled = createStyled({});
+ const Button = styled('button')({});
+
+ const { container } = render();
+
+ expect(container.firstChild).to.have.tagName('a');
+ expect(container.firstChild).to.have.attribute('href', '/');
+ });
+
+ it('able to pass props to `as` styled component', () => {
+ const styled = createStyled({});
+ const ChildRoot = styled('div')({});
+ const Child = ({ component }) => content;
+ const Button = styled('button')({});
+ const { container } = render();
+
+ expect(container.firstChild).to.have.tagName('span');
+ });
+ });
});