1

I'm working on sharing code between a React and a React Native application (as much as possible).

For that I'm using styled-components. I'm trying something like this for the template strings

const BUTTON_STYLE = `
    display: flex;
    flex-direction: row;
    align-items: center;
    align-self: center;
    border-radius: 6px;
    padding: 20px 20px;
    margin: 0 2px;
    background-color: ${(props: ButtonProps) =>
        CUSTOM_BUTTON_STYLES[props.styleType].backgroundColor};
    border: solid 1px ${(props: ButtonProps) => CUSTOM_BUTTON_STYLES[props.styleType].borderColor};
`;

I'm using it like this in web

export const StyledWebButton = styled.button<ButtonProps>`${BUTTON_STYLE}`;

And like this in React Native

export const StyledNativeButton = styled.TouchableOpacity<ButtonProps>`${BUTTON_STYLE}`;

Problem comes when using ${(props: ButtonProps) =>. It does not get props properly. That makes the template string useless because styled components is not passing the component's props properly.

Any ideas?

Thanks!

Javier Manzano
  • 4,761
  • 16
  • 56
  • 86

2 Answers2

1

You should be able to use the "as" polymorphic property to re-use a styled-component's styles but render it as a different element type. Here is an example from the docs:

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

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

I put together a quick CodeSandbox to demonstrate the same problem you're describing with template strings but serve as an example of how using the "as" prop can get around the issue. I even put together a separate component, <LinkButton /> in my example to prevent from having to repeat "as" on every element.

Here is a very reduced example from the sandbox.

const Button = styled.button<ButtonProps>`
  background-color: ${(props: ButtonProps) =>
    CUSTOM_BUTTON_STYLES[props.styleType].backgroundColor};
  border-radius: 5px;
  padding: 0.5rem 0.75rem;
  border: 2px solid #14a0cb;
  color: white;
  font-size: 1rem;
`;

const LinkButton = ({ children, ...rest }) => {
  return (
    <Button as="a" {...rest}>
      {children}
    </Button>
  );
};

const App = () => {
  return (
    <>
      <Button styleType="primary">Click Me</Button>
      <Button styleType="secondary">Click Me</Button>
      <LinkButton styleType="primary">Click Me</LinkButton>
      <LinkButton styleType="secondary">Click Me</LinkButton>
    </>
  )
}
Jacob K
  • 1,096
  • 4
  • 10
  • That's quite cool and I did not know that, but that does not apply for my example, because we have to create react and react native components – Javier Manzano Mar 05 '22 at 22:55
1

You'll want to use the css helper function.

import styled, { css } from "styled-components";

export type ButtonProps = {
  styleType: "blue" | "tamato";
};

const CUSTOM_BUTTON_STYLES = {
  blue: {
    backgroundColor: "blue",
    borderColor: "blue"
  },
  tamato: {
    backgroundColor: "tomato",
    borderColor: "tomato"
  }
};

const BUTTON_STYLE = css`
  color: white;
  display: flex;
  flex-direction: row;
  align-items: center;
  align-self: center;
  border-radius: 6px;
  padding: 20px 20px;
  margin: 0 2px;
  background-color: ${({ styleType }: ButtonProps) =>
    CUSTOM_BUTTON_STYLES[styleType].backgroundColor};
  border: solid 1px
    ${({ styleType }: ButtonProps) =>
      CUSTOM_BUTTON_STYLES[styleType].borderColor};
`;

export const StyledWebButton = styled.button<ButtonProps>`
  ${BUTTON_STYLE}
`;

// export const StyledNativeButton = styled.TouchableOpacity<ButtonProps>`
//   ${BUTTON_STYLE}
// `;

Edit Styled-Components CSS Helper

On that note, I'd highly recommend not mixing react and react-native components within the same file, but to each their own.

Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51