cl0udw4lk3r
cl0udw4lk3r

Reputation: 2733

How to achieve tag agnostic styled components?

If I want a button but, only the presentational part of that, so if I do:

import styled from 'styled-components'

const Button = styled.button`
  color: red;
  text-align: center;
`

I'm forced to render a button tag, but what about if semantically I need an anchor?

Upvotes: 22

Views: 17968

Answers (6)

Beau Smith
Beau Smith

Reputation: 34367

Use the "as" polymorphic prop in v4

copy/pasta from the example in the docs:

const Component = styled.div`
  color: red;
`;

render(
  <Component
    as="button"
    onClick={() => alert('It works!')}
  >
    Hello World!
  </Component>
)

Upvotes: 37

Matthew Bliffert
Matthew Bliffert

Reputation: 31

As @typeoneerror pointed out, styled-components provides WithComponent. You can use this to to create a new component based on a prop containing the tag. Piggybacking off the example, it would look like this:

const _Button = styled.button`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

const Button = ({ as: tag = 'button', children, ...props }) => {

  // We're replacing the <button> tag with whatever tag is assigned to the 'as' prop (renamed to 'tag' and defaulted to button), but reuse all the same styles
  
  const Composed = _Button.withComponent(tag);
  
  // We return the newly-created component with all its props and children
  
  return <Composed {...props}>{children}</Composed>;
};

render(
  <div>
    <Button>Normal Button</Button>
    <Button as='a'>Normal Link</Button>
  </div>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Upvotes: 3

typeoneerror
typeoneerror

Reputation: 56948

styled-components provides withComponent that'll be useful for cases where you want to use an a different tag with a component. This is similar to @siddharthkp's answer in function, but uses the API.

Example from the documentation:

const Button = styled.button`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

// We're replacing the <button> tag with an <a> tag, but reuse all the same styles
const Link = Button.withComponent('a')

// Use .withComponent together with .extend to both change the tag and use additional styles
const TomatoLink = Link.extend`
  color: tomato;
  border-color: tomato;
`;

render(
  <div>
    <Button>Normal Button</Button>
    <Link>Normal Link</Link>
    <TomatoLink>Tomato Link</TomatoLink>
  </div>
);

Upvotes: 11

cl0udw4lk3r
cl0udw4lk3r

Reputation: 2733

I've asked the same question on styled-components issue tracker: https://github.com/styled-components/styled-components/issues/494

And the current "solution" that I've found is:

// agnosticStyled.js
import React from 'react'
import styled from 'styled-components'

export default styled(
  ({tag = 'div', children, ...props}) =>
    React.createElement(tag, props, children)
)

And then when you need it:

import React from 'react'
import styled from './agnosticStyled'

 const Button = styled`
   color: palevioletred;
   text-transform: uppercase;
 `

 export default Button

And finally:

import React from 'react'
import Button from './Button'

const Component = () => 
  <div>
    <Button>button</Button>
    <Button tag="button">button</Button>
    <Button tag="a" href="https://google.com">button</Button>
   </div>

export default Component 

Here a full functioning example: https://codesandbox.io/s/6881pjMLQ

Upvotes: 6

siddharthkp
siddharthkp

Reputation: 71

You can use it with a anchor tag as well, there's nothing stopping you.

import styled from 'styled-components'

const Button = styled.a`
  color: red;
  text-align: center;
`

If you want to keep both, you can reuse the styles by pulling them out:

import styled from 'styled-components'

const styles = `
  color: red;
  text-align: center;
`

const Button = styled.button`
  ${styles}
`

const LinkButton = styled.a`
  ${styles}
`

Upvotes: 7

Raphael Rafatpanah
Raphael Rafatpanah

Reputation: 19967

Since we're just using JavaScript, why not use a function?

const myButtonStyle = (styled, tag) => {
  return styled[tag]`
    color: red;
    text-align: center;
  `
}

const Button = myButtonStyle(styled, 'button')

Upvotes: 2

Related Questions