FortAwesome / Font-Awesome

The iconic SVG, font, and CSS toolkit
https://fontawesome.com
Other
73.72k stars 12.19k forks source link

Feature request: Astro Components + Docs for Astro #20048

Open wavedeck opened 11 months ago

wavedeck commented 11 months ago

Is your feature request related to a problem?

Font-Awesome supports the major frontend frameworks such as React, Vue… as well as some Meta Frameworks like Next.js, but using it with Astro is still unsupported / undocumented.

Feature description

Astro is a unique meta framework focused on websites and has its own component architecture alternative to react, vue… (.astro files)

unfortunately, there is currently no official integration although astro is increasingly getting used over other meta frameworks because of its better performance and focus on websites rather than applications.

I’d like to see an official component for astro components so we don’t have to use react or svg-core

Alternatives

Alternative to an official component, we should at least create some documentation using Font-Awesome with Astro Layouts / Pages.

Additional context

I managed to get Font-Awesome working (client side) via @fortawesome/fontawesome-svg-core and am working on a pure SSR way by using the icon().html function provided by svg-core. This function could also possibly be used for creating an astro component, similar to how the react / vue components work.

Feature request checklist

wavedeck commented 11 months ago

For reference, i managed to make Font-Awesome SVG+JS work on the client side by using @fortawesome/fontawesome-svg-core in my layout component. However, this results in a small flash since the client needs to execute the javascript before an icon appears. It would be best to handle svg-inlining on the server side to minimize this behavior.

This is how to use SVG+JS client-side in Astro:

  1. In the src/layouts/Layout.astro component add the styles / scripts of @fortawesome/fontawesome-svg-core
    
    ---
    // load the core stylesheet on the server side,
    // so astro can bundle, optimize and serve it.
    import '@fortawesome/fontawesome-svg-core/styles.css'
    ---

<!doctype html>

{/* this is where your page content is added */}

{/ Add a client side script for loading Font-Awesome /}


2. and then in your `src/pages/index.astro` add the icon:
```astro
---
import Layout from '../layouts/Layout.astro';
---

<Layout>
    <h1>Hello World</h1>
    {/* add the icon to your page. it will automatically be replaced client-side */}
    <i class="fas fa-phone"></i>
</Layout>
wavedeck commented 11 months ago

I also just tested dom.i2svg() instead of .watch() and can confirm it working even for deeply nested pages, since all astro does is bundle the script using vite and add the optimized script src to the document.

So compatibility with Font-Awesome is definitely there without any major problems. It’s just not documented anywhere how to use it with astro.

I’d love to see my example being incorporated (in some form or another) in the docs or even having the FortAwesome team consider creating an official astro component.

h93xV2 commented 6 months ago

Found this issue after looking into the same problem for myself. Looks like you can do something like this for SSR:

---
import { icon } from '@fortawesome/fontawesome-svg-core';
import { faHouse } from '@fortawesome/free-solid-svg-icons';

const devIcon = icon(faHouse).html;
---

<div set:html={devIcon}></div>

However, you will have to go and set the width and height for the SVG. I'm doing that like this:

.svg-inline--fa {
  width: 1.33em;
  height: 1.33em;
}
b-d-m-p commented 5 months ago

I got a similar setup to @h93xV2 's working with this, but I'm using Shoelace and it's not playing nice with it yet.

carcigenicate commented 4 months ago

H93xV2's solution can be wrapped in a component to make it easier to use: FaIcon.astro

---
import type {IconDefinition} from '@fortawesome/fontawesome-svg-core';
import {icon} from '@fortawesome/fontawesome-svg-core';

interface Props {
  icon: IconDefinition
}

const iconHtml = icon(Astro.props.icon).html;
---
<style>
    .icon {
        display: inline-block;
        width: 1em;
        height: 1em;
    }
</style>
<i class="icon" set:html={iconHtml}></i>

Then, to use it (MDX file here):

import { faUser } from '@fortawesome/free-solid-svg-icons';
import FaIcon from '../../../../components/FaIcon.astro';

<FaIcon icon={faUser} />
Shiva127 commented 2 months ago

Same as @carcigenicate but with Fragment, imported CSS styles and classes.

---
import { icon, type IconDefinition } from "@fortawesome/fontawesome-svg-core";
import "@fortawesome/fontawesome-svg-core/styles.css";

interface Props {
  icon: IconDefinition;
  class?: string;
}

const iconHtml = icon(Astro.props.icon, {
  classes: Array.from(new Set(Astro.props.class?.split(" "))),
}).html;
---

<Fragment set:html={iconHtml} />

And for layer.

---
import { layer, icon, type IconDefinition } from "@fortawesome/fontawesome-svg-core";
import "@fortawesome/fontawesome-svg-core/styles.css";

interface IconsDefinition {
  icon: IconDefinition;
  class?: string;
}

interface Props {
  icons: IconsDefinition[];
  class?: string;
}

const layerHtml = layer((push) => {
  for (const iconDefinition of Astro.props.icons) {
    push(icon(iconDefinition.icon, {
      classes: Array.from(new Set(iconDefinition.class?.split(" "))),
    }));
  }
}, {
  classes: Array.from(new Set(Astro.props.class?.split(" "))),
}).html;
---

<Fragment set:html={layerHtml} />