Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom icons #477

Open
logusgraphics opened this issue Dec 19, 2018 · 19 comments
Open

Custom icons #477

logusgraphics opened this issue Dec 19, 2018 · 19 comments
Labels
enhancement New feature or request

Comments

@logusgraphics
Copy link

Based on a custom SVG icon file I have, can styled icons be used as a mechanism to embed custom icons? How could I implement this feature in my project?

@jacobwgillespie
Copy link
Member

Hey @logusgraphics this isn't possible currently, but I've started work on the next version of this package which will have the ability to create custom icons alongside the built-in ones. So stay tuned... 🙂

@jacobwgillespie
Copy link
Member

Hey just wanted to let you know I haven't forgotten about this issue - custom icons aren't quite ready yet, but the latest release (v7.1.0) starts to lay the groundwork for supporting custom icons.

If you'd like to provide some API feedback, you can try importing from an internal StyledIconBase component. I'd be curious to know how custom icons would be used in the wild.

DISCLAIMER: this is an internal API and will likely break in a future release - you probably don't want to use this in production. This is not the final API.

For instance, if this is your SVG:

<svg viewBox="0 0 24 24" fill="currentColor">
  <path fill="currentColor" d="M13 4c-3.859 0-7 3.141-7 7 0 .763.127 1.495.354 2.183l-.749.75-.511.512-1.008 1.045a3.076 3.076 0 0 0-.891 2.185 3.134 3.134 0 0 0 3.13 3.131c.757 0 1.504-.278 2.104-.784l.064-.055.061-.061 1.512-1.51.75-.749A6.983 6.983 0 0 0 13 18c3.859 0 7-3.141 7-7s-3.141-7-7-7zm0 12c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5zm0-9c-2.205 0-4 1.794-4 4s1.795 4 4 4 4-1.794 4-4-1.795-4-4-4zm0 7a3.001 3.001 0 0 1 0-6 3.001 3.001 0 0 1 0 6z" />
</svg>

You could create a custom icon like so:

import * as React from 'react'
import {StyledIconBase} from 'styled-icons/StyledIconBase'

const CustomIcon = React.forwardRef((props, ref) => {
  // Additional attributes to add to the <svg> tag
  const attrs = {
    fill: 'currentColor',
  }

  return (
    <StyledIconBase
      iconAttrs={attrs}
      iconVerticalAlign="middle"
      iconViewBox="0 0 24 24"
      {...props}
      ref={ref}
    >
      <path fill="currentColor" d="M13 4c-3.859 0-7 3.141-7 7 0 .763.127 1.495.354 2.183l-.749.75-.511.512-1.008 1.045a3.076 3.076 0 0 0-.891 2.185 3.134 3.134 0 0 0 3.13 3.131c.757 0 1.504-.278 2.104-.784l.064-.055.061-.061 1.512-1.51.75-.749A6.983 6.983 0 0 0 13 18c3.859 0 7-3.141 7-7s-3.141-7-7-7zm0 12c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5zm0-9c-2.205 0-4 1.794-4 4s1.795 4 4 4 4-1.794 4-4-1.795-4-4-4zm0 7a3.001 3.001 0 0 1 0-6 3.001 3.001 0 0 1 0 6z" />
    </StyledIconBase>
  )
})

CustomIcon.displayName = 'CustomIcon'

And if you use TypeScript and want the types to match:

import * as React from 'react'
import {StyledIconBase} from 'styled-icons/StyledIconBase'

const CustomIcon = React.forwardRef<SVGSVGElement, StyledIconProps>((props, ref) => {
  const attrs: React.SVGProps<SVGSVGElement> = {
    fill: 'currentColor',
  }

  return (
    <StyledIconBase
      iconAttrs={attrs}
      iconVerticalAlign="middle"
      iconViewBox="0 0 24 24"
      {...props}
      ref={ref}
    >
      <path fill="currentColor" d="M13 4c-3.859 0-7 3.141-7 7 0 .763.127 1.495.354 2.183l-.749.75-.511.512-1.008 1.045a3.076 3.076 0 0 0-.891 2.185 3.134 3.134 0 0 0 3.13 3.131c.757 0 1.504-.278 2.104-.784l.064-.055.061-.061 1.512-1.51.75-.749A6.983 6.983 0 0 0 13 18c3.859 0 7-3.141 7-7s-3.141-7-7-7zm0 12c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5zm0-9c-2.205 0-4 1.794-4 4s1.795 4 4 4 4-1.794 4-4-1.795-4-4-4zm0 7a3.001 3.001 0 0 1 0-6 3.001 3.001 0 0 1 0 6z" />
    </StyledIconBase>
  )
})

CustomIcon.displayName = 'CustomIcon'

That CustomIcon would have all the behaviors and properties of any other Styled Icon and would also match the StyledIcon TypeScript type, in case you have any components that accept an icon as a prop.


Again, that's an internal API, and the final custom icon API will likely be considerably simpler, like createStyleIcon(<svg>...</svg>) or even something like a Webpack loader, but I would appreciate any feedback on potential use-cases or desired features.

@logusgraphics
Copy link
Author

I was using a similar approach/pattern for custom icons. I created a base component and then the icon was the SVG passed as props. Will look into it deeper to see how it could integrate well with the pattern you're proposing. Thanks.

@meshelton
Copy link

Would it be possible to extract the component generation code into a cli so custom icons could be made as a step in the build process?

@jacobwgillespie
Copy link
Member

Would it be possible to extract the component generation code into a cli so custom icons could be made as a step in the build process?

Most likely yes - I've been slowly working on this feature, and after complete, Styled Icons will read the icon SVG data as a JSON file, so it should be fairly straightforward to have a CLI that converts existing SVG files into that JSON format.

@nathanvale
Copy link

Hey there any chance this feature is nearing production ready?

@LoganArnett
Copy link

Any more updates on this feature?

@ashbuilds
Copy link

I guess not ready for me yet. Going for my old style ico-moon, Really interested in this once ready.

@stramel
Copy link
Contributor

stramel commented Sep 23, 2019

@jacobwgillespie I was looking at taking a stab at this. Was just curious if you had any other guidance you might want the API to look like?

Would createIcon take a JSX.Element or a string?
Would you want to cover SVG optimizing or leave that to the users?

@jacobwgillespie
Copy link
Member

I was looking at taking a stab at this

Awesome, very appreciated! 🎉

Would createIcon take a JSX.Element or a string?
Would you want to cover SVG optimizing or leave that to the users?

I don't have any strong preferences here, though we'd potentially want to avoid shipping an SVG optimizer into the client bundle. Right now the optimized SVG data is passed to StyledIconBase here: https://github.com/jacobwgillespie/styled-icons/blob/master/packages/styled-icons/generate/templates/StyledIconBase.tsx. We could formalize that API.

But overall I'm open to any ideas and don't have any prescriptions!

@stramel
Copy link
Contributor

stramel commented Sep 23, 2019

I don't have any strong preferences here, though we'd potentially want to avoid shipping an SVG optimizer into the client bundle. Right now the optimized SVG data is passed to StyledIconBase here: /packages/styled-icons/generate/templates/StyledIconBase.tsx@master . We could formalize that API.

I agree that shipping an optimizer is less than desirable. I would opt to leave that up to the user.

I'm not sure what the common use-case is here for JSX.Element vs string. Does anyone have any examples? JSX.Element would definitely be easier to accept. A string would require something like dangerouslySetInnerHTML to insert the string.

@stramel stramel added the enhancement New feature or request label Feb 11, 2020
@Haraldson
Copy link

Haraldson commented Mar 13, 2020

import React from 'react'
import { StyledIconBase } from 'styled-icons/StyledIconBase'
import TeamSvg from 'static/gfx/svg/team.csvg'

// Generic handling
export const Custom = ({ svg, size = 24, className, ...props }) => {
    const { props: svgProps } = svg()
    const { children, ...attrs } = svgProps

    return (
        <StyledIconBase
            width={size}
            height={size}
            iconVerticalAlign="middle"
            {...attrs}
            {...props}
            {...(className ? { className } : {})}>
            {children}
        </StyledIconBase>
    )
}

// Specific icons
export const Team = props => (
    <Custom
        {...props}
        svg={TeamSvg} />
)

Just got this working quite nicely with the @svgr/webpack plugin. Calling the SVG component as a function feels kind of dirty, but it works.

Is it as dirty as it feels, or is it alright? Also, was any progress made on this issue since last year, removing the need for this hack?

@jw-miaem
Copy link

Any movement on this? would love to have this feature

@zverexe
Copy link

zverexe commented Nov 23, 2020

any updates on this?

@jacobwgillespie
Copy link
Member

Hey all, no updates yet, but if you had to describe how you'd like this feature to work, I'd appreciate any feedback here! I haven't personally had the need to add custom icons to Styled Icons, so any insights into what you'd expect would be extra helpful!

@Haraldson
Copy link

@jacobwgillespie For me personally it’s about replicating

  1. the visual appearance and behavior
  2. all the accessibility stuff you provide under the hood

The visual stuff is mostly about making sure sizes are consistent; a custom icon should use the same amount of its bounding box as the bundled icons, and of course be centered.

The accessibility stuff is mostly about having sane defaults applied; basically to make it possible to use the same API as with the bundled icons.

@jnelken
Copy link

jnelken commented Jan 24, 2021

It's pretty simple IMO -- make any svg behave like a styled-icon! For instance, i have pictures of fruits I would like to incorporate as svgs into my app. BTW we appreciate your help on this!

@muchisx
Copy link

muchisx commented May 29, 2023

@jacobwgillespie @Haraldson @stramel has this been implemented already in some other way and I didn't notice? This is the only thread I could find about this matter

@muchisx
Copy link

muchisx commented May 30, 2023

For anyone working with Vite, I used the "vite-plugin-svgr" for vite react and did this, is working so far, if anyone notices an error or something to improve, let me know !

import { StyledIconBase } from '@styled-icons/styled-icon';
import type { Props } from './CustomIcon.types';

// This uses the type of the icon from @svgr/core
// Icon must be called in this way
// import { ReactComponent as customName } from './icon-location.svg';
// and passed as prop to this component as svgrIcon

function CustomIcon(props: Props) {
  const { size = 24, className, svgrIcon } = props;
  const { children, ...attrs } = svgrIcon({ className: 'custom-icon' })?.props || {};

  return (
    <StyledIconBase width={size} height={size} iconVerticalAlign="middle" className={className} {...attrs}>
      {children}
    </StyledIconBase>
  );
}

CustomIcon.displayName = 'CustomIcon';

export default CustomIcon;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.