Moshe
Moshe

Reputation: 6991

Styling Nested Components in Styled-Components

I have created a Dropdown Component in React using Styled Components. Here is a simplified outline of the component:

const Dropdown = (
    <DropdownBase>
      <Trigger>
        {title}
      </Trigger>
      <Submenu>
        {children}
      </Submenu>
    </DropdownBase>
  )

const DropdownBase = styled.div`
  /* Default Styles */
`

const Trigger = styled(Link)`
  /* Default Styles */
`

const Submenu = styled.div`
  /* Default Styles */
`

Now, when I import and use the component I want to be able to override the default styles of the nested components (i.e., DropdownBase, Trigger and Submenu). And I want to be able to override those default styles using Styled Components. The problem is, that I do not import those nested components -- I only import the Dropdown component -- like this:

import { Dropdown } from '../path/to/dropdown'

<Dropdown />

So I am wondering, how can I override those nested components when I import the parent component using Styled Components?

Upvotes: 20

Views: 33998

Answers (4)

BraisC
BraisC

Reputation: 299

Extending the answer by @Appel21, I would do something like this using dot notation:

import styled from 'styled-components'

export const Dropdown = () => (
    <DropdownBase>
      <Trigger>
        {title}
      </Trigger>
      <Submenu>
        {children}
      </Submenu>
    </DropdownBase>
  )

const DropdownBase = styled.div`
  /* Default Styles */
`

const Trigger = styled(Link)`
  /* Default Styles */
`

const Submenu = styled.div`
  /* Default Styles */
`

Dropdown.base = DropdownBase;
Dropdown.trigger = Trigger;
Dropdown.subMenu = Submenu;

And then to use it:

import { Dropdown } from '../path/to/dropdown' 
import styled from 'styled-components'
    
const MyComponent = () => {
  return <StyledDropdown />
}

const StyledDropdown = styled(Dropdown)`
  ${Dropdown.base} {
    // custom styles
  }

  ${Dropdown.trigger} {
    // custom styles
  }

  ${Dropdown.submenu} {
    // custom styles
  }
`

This way you have to export only one component and you get autocompletion of the subcomponents, knowing perfectly what subcomponents you can style :)

Upvotes: 2

Appel21
Appel21

Reputation: 246

The best way to do this would be to export DropdownBase, Trigger, and Submenu from your Dropdown component, then import them along with Dropdown and override this like this:

import { Dropdown, DropdownBase, Trigger, Submenu } from '../path/to/dropdown'
import styled from 'styled-components'

const MyComponent = () => {
  return <StyledDropdown />
}

const StyledDropdown = styled(Dropdown)`
  ${DropdownBase} {
    // custom styles
  }

  ${Trigger} {
    // custom styles
  }

  ${Submenu} {
    // custom styles
  }
`

This works well because it targets the specific child styled components.

Alternatively, you could target them based on their tag or child order, but this may fail if you make updates to the Dropdown component.

Upvotes: 17

Johnny Zabala
Johnny Zabala

Reputation: 2465

How about this:

const Dropdown = (
    <DropdownBase className={dropdownBaseClassName}>
      <Trigger className={triggerClassName}>
        {title}
      </Trigger>
      <Submenu className={submenuClassName}>
        {children}
      </Submenu>
    </DropdownBase>
  )
import { Dropdown } from '../path/to/dropdown'

<StyledDropdown />

const StyledDropdown = styled(Dropdown).attrs({ dropdownBaseClassName:..., triggerClassName:..., submenuClassName:... })`
.${dropdownBaseClassName} {
// styles
}
.${triggerClassName} {
// styles
}
.${submenuClassName} {
// styles
}

Upvotes: 2

Ted Brownlow
Ted Brownlow

Reputation: 1117

It looks like themes are what you want.

import { render } from "react-dom"
import { ThemeProvider } from "styled-components"

const Dropdown = (
    <DropdownBase>
      <Trigger>
        {title}
      </Trigger>
      <Submenu>
        {children}
      </Submenu>
    </DropdownBase>
  )

const defaultTheme = {color:'black'}
const specificTheme = {color:'red'}

const DropdownBase = styled.div`
  /* Default Styles */
  color:${props=>props.theme.color};
`

const Trigger = styled(Link)`
/* Default Styles */
color:${props=>props.theme.color};
`

const Submenu = styled.div`
  /* Default Styles */
  color:${props=>props.theme.color};
`

render(<ThemeProvider theme={defaultTheme}>
    <div>
        <Dropdown>Your default dropdown</Dropdown>
        <div>
            Your hierarchy
            <ThemeProvider theme={specificTheme}>
                <Dropdown>Your custom dropdown</Dropdown>
            </ThemeProvider>
        </div>
    </div>
</ThemeProvider>)

Upvotes: -1

Related Questions