I am trying to override the style of a component inside another component.
So, I have a component A, with some div's inside(Wrapper, Header).
In a new component, I am trying to override component A. Inside that override I want some new styling to the Header component. I know I can reference a component inside the same component but I can't find any info about referencing inside a new component.
// Component A
import React from "react";
export default ({
className,
title
}) => (
<Wrapper className={className}>
<Header>{title}</Header>
</Wrapper>
)
);
const Header = styled.h2`
padding-left: 0;
`;
// Component B
import React from "react";
export default () => (
<CustomA>
/* content */
</CustomA>
)
);
const CustomA = styled(<A />)`
${Header} {
padding-left: 20px;
}
`;
I expect Header to be changed but I get "Header is not defined".
There are a couple of issues to address.
You can follow along on CodeSandbox.
1. Export Header component from Component A
You need to make Header component available outside Component A so that it can be referenced within Component B.
import React from "react";
import styled from "styled-components";
export const Header = styled.h2`
padding-left: 0;
`;
export default ({ className = "", title }) => (
<div className={className}>
<Header>{title}</Header>
</div>
);
2. Errors in Component B
There are three issues here.
You need to pass the component name, not the instance to styled() function.
Instead of const CustomA = styled(<A />) where <A /> is an instance,
Do const CustomA = styled(A).
You need to import Header component exported from Component A.
Now you can reference is within styled(A) as ${Header}.
import styled from "styled-components";
import A, { Header } from "./CustomA";
const CustomA = styled(A)`
${Header} {
padding-left: 20px;
}
`;
export default () => <CustomA title="Component B Content" />;
The last issue is that, you aren't passing the title (I also did className = "" in Component A to make it optional).
First of all you need to use styled like below:
const CustomA = styled(A)``;
instead of
const CustomA = styled(<A/>)``;
Secondly, try the following code:
const CustomA = styled(A)`
h2{
padding-left: 20px;
}
`;
try
const CustomA = styled(A)`
padding-left: 20px;
`;
Related
i'm new to react and im trying to add render a list of items from an external SidebarData.js file (in the same root /components/..)
i'm not sure why my map function is not returning anything.
i get a list of elements thats correct, but the item.title and item.path seem not to render...
I feel there's a problem with the props.
I tried to write just
render(){
<h1>{SubmenuData[1].title}</h1>
}
and it works fine, but when i try to map on the full array, it doesn't seem to render anything. it renders the correct number of elements, but the title and path are not returning...
Here's my two components : Sidebar (Main one)
import React from 'react'
import styled from 'styled-components'
import { SidebarData } from './SidebarData'
import Submenu from './Submenu'
const Nav = styled.div`
background: #f5f5f5;
color: #7d7d7d;
display:flex;
justify-content:flex-start;
height:100%;
width:15%;
`
const Sidebar = () => {
return (
<>
<Nav>
{SidebarData.map((item, index)=>{
return <Submenu item={item} key={item.index} />
})}
</Nav>
</>
)
}
export default Sidebar (Where i think there's a problem)
and Submenu
import React, { Component } from 'react'
import styled from 'styled-components'
import { Link } from "react-router-dom"
const SidebarLink = styled(Link)`
display: flex;
color: #404040;
`
const SidebarLabel = styled.span`
color:#000;
`
const Submenu = (item)=>{
return (
<SidebarLink to={item.path} >
<SidebarLabel>{item.title}</SidebarLabel>
</SidebarLink>
)
}
export default Submenu
Your style of receiving props is mistake i guess. Destructure the props like:
const Submenu = ({item})=>{
return (
<SidebarLink to={item.path} >
<SidebarLabel>{item.title}</SidebarLabel>
</SidebarLink>
)
}
export default Submenu
I thought this was possible with styled components
Using the first styled Component Block as the bases for another component like
export const BlockOne = styled.Block
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import styled from 'styled-components'
export const Block = styled.div`
width: 100px;
height: 100px;
background: red;
`
export const BlockOne = styled.Block`
background: yellow;
`
const App = () => {
return (
<div>
<Block/>
<BlockOne/>
</div>
);
}
render(<App />, document.getElementById('root'));
Is there a way to do this
Yes, like this
export const BlockOne = styled(Block)`
background: yellow;
`
styled-component only has basic components (tags such as div, span, etc.) as attributes. For anything else, you pass it as a prop.
If you pass a custom component to it, make sure that it accepts a className and that it passes it down to a div or something:
const MyComponent = ({className}) => {
return <div className={className}></div> // styled-component will use this classname to apply the style
}
export const MyStyledComponent = styled(MyComponent)`
background: yellow;
`
Else it would have no effect.
How can I overwrite default headings globally with styled components theme?
For colors I'm doing:
import React from "react";
import { ThemeProvider } from "styled-components";
const theme = {
colors: {
main: "#8563FF",
mainLight: "#AB93FF",
.....
}
};
const Theme = ({ children }) => (
<ThemeProvider theme={theme}>{children}</ThemeProvider>
);
export default Theme;
and them in my component I'm getting those colors trough props
${props => props.theme.colors.main}
I would like to overwrite default headings eg. h1 with my custom fontSize, weight..., so that when I'm using it in my component it looks like this:
const StyledH1 = styled.h1`
/* additional local styling for overwritten h1 */
color: ${props => props.theme.colors.main};
marginTop: 20px;
`;
On top of that, it should contains media query inside overwritten heading.
How to add overwrites to theme?
You should use createGlobalStyle and ensure that its ThemeProvider's child.
A helper function to generate a special StyledComponent that handles global styles.
const GlobalStyle = createGlobalStyle`
h1 {
color: ${props => props.theme.colors.main};
margin-top: 20px;
}
`
// will have margin-top
styled.h1``
<ThemeProvider>
<GlobalStyles />
</ThemeProvider>
You can create reset styles with createGlobalStyle and put it in the root of your project.
For example here are reset styles for all HTML attributes, you can import it in the root index file, and in the project, all default styles of HTML tags will be reset. (styles like margins of <p> or <h1> and so on)
You can do some modifies to set default values of some attributes, for example like the default color of <p>, <h1> or others
Examples of reset.css
https://gist.github.com/DavidWells/18e73022e723037a50d6
https://cssreset.com/scripts/eric-meyer-reset-css/
I would like to pass a custom prop (exactly: theme name as string) to each passed styled component through Provider, so it was available throughout the css definition.
ThemeProvider almost does it, but it expects object, not the string. I do not want to pass whole object with theme settings, just the name of my theme.
I do not want to use special theme prop or similar, because then I would have to it manually every single time I create new styled component. Provider seems like the best option if only it cooperated with string.
Is there any possibility to pass a string through Provider to Consumer builded in styled components?
EDIT:
[PARTIAL SOLUTION]
I found what I was looking for when I realized styled-components exports their inner context. That was it. Having access to pure react context gives you original Provider, without any 'only objects' restriction ('only objects' is a styled-components custom provider restriction).
Now I can push to each styled component exactly what I want and if I want.
import styled, { ThemeContext } from 'styled-components';
const StyledComponent = styled.div`
color: ${props => props.theme == 'dark' ? 'white' : 'black'};
`;
const Component = props => {
const theme = 'dark';
return (
<ThemeContext.Provider value={theme}>
<NextLevelComponent>
<StyledComponent />
</NextLevelComponent>
</ThemeContext.Provider>
);
};
Hope I have this correct, from what I've been able to glean. I haven't tried this out but it seems it might work for you. This is lifted directly from the reactjs.org docs regarding context. It passed the string name of the theme down.
const ThemeContext = React.createContext('green');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="blue">
<SomeComponent />
</ThemeContext.Provider>
);
}
}
function SomeComponent(props) {
return (
<div>
<OtherComponent />
</div>
);
}
class OtherComponent extends React.Component {
static contextType = ThemeContext;
render() {
return <ThirdComponent theme={this.context} />
}
}
I hope this helps you understand the idea behind ThemeContext from styled-components. I've passed string "blue" to ThemeContext just to show, that it should not be object and you can use just string.
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styled, { ThemeContext } from "styled-components";
// Define styled button
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
color: ${props => props.theme};
border: 2px solid ${props => props.theme};
`;
// Define the name of the theme / color (string)
const themeName = "blue";
const ThemedButton = () => {
// Get the name from context
const themeName = useContext(ThemeContext);
return <Button theme={themeName}>Themed color: {themeName}</Button>;
};
function App() {
return (
<div className="App">
<ThemeContext.Provider value={themeName}>
<ThemedButton />
</ThemeContext.Provider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Demo: https://codesandbox.io/s/styled-components-example-with-themecontext-cso55
Within my React+StyledComponent app, I have a theme file like so:
theme.js:
const colors = {
blacks: [
'#14161B',
'#2E2E34',
'#3E3E43',
],
};
const theme = {
colors,
};
export default theme;
Currently, I can easily use these colors to style my components like so:
const MyStyledContainer = styled.div`
background-color: ${(props) => props.theme.colors.blacks[1]};
`;
The problem is, how do I pass blacks[1] to a Component as the prop of the color to use like so:
<Text color="black[1]">Hello</Text>
Where Text.js is:
const StyledSpan = styled.span`
color: ${(props) => props.theme.colors[props.color]};
`;
const Text = ({
color,
}) => {
return (
<StyledSpan
color={color}
>
{text}
</StyledSpan>
);
};
Text.propTypes = {
color: PropTypes.string,
};
export default Text;
Currently the above is silently failing and rending the following in the DOM:
<span class="sc-brqgn" color="blacks[1]">Hello</span>
Any ideas on how I can get this to work? Thank you
EDIT: Updated to use styled-components withTheme HOC
New answer
You could wrap the component rendering <Text> in the higher order component (HOC) withTheme provided by styled-components. This enables you to use the theme given to the <ThemeProvider> directly in the React component.
Example (based on the styled-components docs):
import React from 'react'
import { withTheme } from 'styled-components'
import Text from './Text.js'
class MyComponent extends React.Component {
render() {
<Text color={this.props.theme.colors.blacks[1]} />;
}
}
export default withTheme(MyComponent)
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
Old answer
You could import the theme where you render and pass <Text color={theme.blacks[1]} />.
import theme from './theme.js'
...
<Text color={theme.colors.blacks[1]} />
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
You can use defaultProps
import PropTypes from 'prop-types'
MyStyledContainer.defaultProps = { theme }
App.js
App gets theme and passes color to Text
import React, { Component } from 'react'
import styled from 'styled-components'
const Text = styled.div`
color: ${props => props.color || 'inherit'}
`
class App extends Component {
render() {
const { theme } = this.props
return (
<Text color={theme.colors.black[1]} />
)
}
}
export default App
Root.js
Root component passes theme to entire application.
import React, { Component } from 'react'
import { ThemeProvider } from 'styled-components'
import theme from './theme'
import App from './App'
class Root extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
)
}
}
export default Root
If you're using functional components in React and v4.x and higher styled-components, you need to leverage useContext and styled-components' ThemeContext. Together, these allow you to use your theme settings inside of components that aren't styled-components.
import { useContext } from 'react'
import { ThemeContext } from 'styled-components'
export default function MyComponent() {
// place ThemeContext into a context that is scoped to just this component
const themeProps = useContext(ThemeContext)
return(
<>
{/* Example here is a wrapper component that needs sizing params */}
{/* We access the context and all of our theme props are attached to it */}
<Wrapper maxWidth={ themeProps.maxWidth }>
</Wrapper>
</>
)
}
Further reading in the styled-components docs: https://styled-components.com/docs/advanced#via-usecontext-react-hook