Styled-components add styles to custom component - reactjs

I want to add styles to the custom component with a help of the styled-components library. I found some similar questions on StackOverflow with answers but it seems not to work for me. I do something like this:
Here is my custom component
const ComponentStyledBox = styled.div`
color: red;
`;
const ComponentStyled = () => (
<ComponentStyledBox>Component With StyledComponents</ComponentStyledBox>
);
And here I try to use my custom component and add some styles to it
const ComponentStyledWrapper = styled(ComponentStyled)`
background-color: green;
`;
export default function App() {
return (
<div>
<ComponentStyledWrapper />
</div>
);
}
As the result, I don't have a green background. Only red text. The same will be if I use instead of the styled custom component just a component with styles added via CSS in a common way. What do I do wrong?
For your convenience here is a CodeSandbox
Thanks in advance!

The problem is caused by this:
const ComponentStyled = () => (
<ComponentStyledBox>Component With StyledComponents</ComponentStyledBox>
);
You've defined a new component called ComponentStyled which is not style-able. Try this instead (in App.styles.tsx):
import { ComponentStyledBox } from "./components/ComponentStyled/ComponentStyled.styles";
export const ComponentStyledWrapper = styled(ComponentStyledBox)`
background-color: green;
`;
ComponentStyledBox is style-able, so you'll get the green background.
Edit (response to #zakharov.arthur's comment): If really do want a custom component with hard-coded children (are you sure this is what you want?), you can make your custom component style-able by exposing ComponentStyledBox's props:
// I haven't actually tried this but it should work.
const ComponentStyled = (props: Omit<Parameters<ComponentStyledBox>[0], 'children'>) =>
<ComponentStyledBox {...props}>Component With StyledComponents</ComponentStyledBox>

Related

ReactJS and Less, set dynamic class

I have a component like this:
import '../MLExtraIcon.less';
const MLExtraIcon = ({ text, color }) => (
<span className={`ml-extra-icon ml-extra-icon-color-${color}`}>
{text}
</span>
);
So, I call <MLExtraIcon> components with 2 props: text and color
But currenly, I have hardcoded the "color" as a class in a less file which is:
// MLExtraIcon.less
#import "../colors";
.ml-extra-icon {
&.ml-extra-icon-color-passion {
background-color: #passion;
}
}
And in the imported colors file I have a bunch of variables like:
// colors.less
#opportunities: #F47521;
#innovation: #1976BD;
#passion: #CB198A;
// etc...
How could I select dynamically the variable to use in the .less file? I mean, my problem is here:
.ml-extra-icon {
&.ml-extra-icon-color-passion {. <--- that's hardcoded, I would like to know if
background-color: #passion;
}
}
...if there's a way to give the variable name as a parameter, for instance:
.ml-extra-icon {
&.ml-extra-icon-color-$color {
background-color: $color;
}
}
(I'm inventing that syntax, in order to explain what I want to achieve). I don't want to create different css clases for each already declared less variable (like the hardcoded example)
Any tip?
If you want to do it dynamic you can use styled-components. Example codesandbox
import React from "react";
import styled from "styled-components";
export default function App() {
const ColorWrapper = styled.span`
color: ${(props) => props.color};
`;
const MLExtraIcon = ({ text, color }) => (
<ColorWrapper color={color}>{text}</ColorWrapper>
);
return (
<div className="App">
{MLExtraIcon({ text: "Your passion", color: "blue" })}
</div>
);
}
Also you can create global theme file instead of local const theme, to use it throughout the project.

Next.js: How to get applied styles from element?

Let's say I have global.css
.test {
background-color: black;
width: 50px;
height: 50px;
}
For some reason, I need to get the styles data from applied element. I tried refs but it always return empty string.
import { useState, useEffect, useRef } from "react";
const IndexPage = () => {
const divEl = useRef<HTMLDivElement | null>(null);
const [divStyle, setDivStyle] = useState({} as CSSStyleDeclaration);
useEffect(() => {
if (divEl.current) {
setDivStyle(divEl.current.style);
}
}, [divEl.current]);
return (
<div>
<div ref={divEl} className="test"></div>
<pre>{JSON.stringify(divStyle, undefined, 2)}</pre>
</div>
);
};
export default IndexPage;
Is it because next.js SSR or should I add something to dependency array?
code sandbox here
You can use computed styles to get what you need, although it won't be a "simple" object with properties. You'll need to query each property individually:
if (divEl.current) {
setDivStyle(getComputedStyle(divEl.current));
}
and then you can do something like:
divStyle.getPropertyValue("background-color")
Here's an example sandbox (forked from yours) that demostrates it.

How to keep styled-components styles separate in different higher-order components on partially static site?

I'm using higher order components to reuse React/Next.js components but with different styles. However, this works only for vanilla React where pages are not generated either server-side or statically. If some pages are static and others dynamic, it won't work.
Here's an example component. We'll focus on TabStyles.Main in a little while:
const Tabs: NextPage<Props> = ({ ... }: Props) => {
...
return (
<TabStyles.Main>
...
</TabStyles.Main>
);
};
export default Tabs;
Here're its styles:
import styled from "styled-components";
const Main = styled.div`
display: flex;
flex-direction: column;
`;
...
export const TabStyles = {
Main,
...
};
Now let's implement two HOCs.
In red.tsx, rendered at path /red, color is red:
// Import Tabs and TabStyles
const TabsFontRed = <P extends {}>(Component: NextPage<P>) => {
TabStyles.Main = styled(TabStyles.Main)`
color: red;
`;
const NewComp = (props: P) => <Component {...props} />;
return NewComp;
};
const RedHOC = TabsFontRed(Tabs);
In blue.tsx, rendered at path /blue, color is blue.
// Import Tabs and TabStyles
const TabsFontBlue = <P extends {}>(Component: NextPage<P>) => {
TabStyles.Main = styled(TabStyles.Main)`
color: blue;
`;
const NewComp = (props: P) => <Component {...props} />;
return NewComp;
};
const BlueHOC = TabsFontBlue(Tabs);
This works if all pages are dynamic because TabStyles.Main = styled(TabStyles.Main)... is called every time we load the page, thus changing color to the correct one.
However, it seems if /red is static but /blue isn't, as we might do with Next.js, the behaviour will be different:
Go to /red first, font is red. Good.
Go to /blue, font is blue. Also good.
Go back to /red, font is now blue. Oh no!
Is there a way to augment this higher order component plus styled-components pattern so that styling will remain different when some pages are static and some are not?
Edit, 1 April 2021:
React complains in the console if we do this. I lost the notice now but it makes me think this is a bad idea. Probably, overriding styles is not good use of higher order components. Now I think HOCs should be used for adding logic, not styles.
Override styled-components in conventional fashion like this:
const NewStyledComp = styled(OlderStyledComp)`
background-color: ...;
`
Or like this:
const NewStyledComp = styled.div`
${OlderStyledComp} {
background-color: ...;
}
`

Semantic UI styled issue

I can add some styles to the Menu component. But how can I add style to the Menu.Item component?
const LayoutHeader = styled(Menu)`
&& {
background: red;
}
`
Use this:
const LayoutHeaderItem = styled(Main.Item)`
&&&{
/* your code */
}
`;

How to create dynamic styled component?

What I'm trying to achieve is something like this:
import styled from 'react-emotion'
const StyledComponent = styled(({tag}) => tag)`
// some css styles
`
And use it like:
<StyledComponent tag=div"/>
<StyledComponent tag="p"/>
<StyledComponent tag="ul"/>
// etc
My expectation was that it should generate HTML as follows:
<div class="some-class"></div>
<p class="some-class"></p>
<ul class="some-class"></ul>
Actual output:
div
p
ul
My question is, can this be achieved or am I missing anything?
Seems like I have figured out a solution to my problem. Sharing my answer for those who may run into the same problem.
I changed StyledComponent declaration to following:
import styled from 'react-emotion'
const StyledComponent = styled(({tag, children, ...props}) => React.createElement(tag, props, children))`
// some css styles
`
This is working as expected.
If anyone has the better answers please do post. Thank you
You are using react-emotion in wrong way, please try this.
const StyledComponent = ({ tag, children, ...props }) => {
const Container = styled(tag)`
background-colo: red;
`;
return <Container {...props}>{children}</Container>;
};
Demo: https://codesandbox.io/s/lr4xxp3757
What worked form me was using the "as" polymorphic prop (https://styled-components.com/docs/api#as-polymorphic-prop):
const StyledComponent = styled.div``
type ComponentProps = {
tag?: AnyStyledComponent
children: React.ReactNode
}
const Component = ({ tag, children }: ComponentProps) => (
<StyledComponent as={tag}>{children}</StyledComponent>
)
Declaring the styled component outside of the function component will spare us from console warnings such as this one: https://github.com/styled-components/styled-components/issues/3117

Resources