<!--
  canonical-url: https://www.avonture.be/blog/docusaurus-bluesky-share
  published:     2025-08-13
  This is a static plain-Markdown mirror generated at build time.
  Visit the canonical URL above for the fully rendered page, with images and interactive components.
-->

# Create our own Docusaurus React component and provide a "Share on Bluesky" button

> Build a custom Docusaurus React component that adds a “Share on Bluesky” button to your blog posts.

---
![Create our own Docusaurus React component and provide a "Share on Bluesky" button](/img/v2/docusaurus_bluesky.webp)

**TL;DR**

This post guides you through creating a 'Share on Bluesky' button for your
Docusaurus blog. You'll learn how to use Docusaurus's swizzle feature to
override the default blog post theme, create a custom React component for the
button, and then integrate it into your blog posts. We'll build the component
step-by-step, including the necessary CSS and SVG icon.

Nine months ago, I had enabled the feature to write comments under my articles by using **Giscus** (see [Adding a comment area](/blog/site-creation)) but only people having a GitHub account were eligible to use that feature and so, almost no one did it.

So I went nearly nine months without giving readers of this blog the opportunity to interact with me, which is a bit frustrating and prevents us from exchanging ideas and enriching each other's lives.

For a few weeks now, I've been present on [Bluesky](https://bsky.app/profile/avonture.be) and ... why not adding a *Share on Bluesky* button below each article?

This idea is neither revolutionary nor complicated so, let's play...

In this first article in a series of two, we'll see how we can create our Docusaurus React component. Before writing this article, I just didn't know how and now, yes, it's so easy.

**Quick Jump:** [See it in Action](#result) | [All Files at a Glance](#all-files-at-a-glance)

## Result

And, tadaaa 🎉🎉🎉, here is a "Share on Bluesky" button in action, below a blog post:

> Screenshot — http\://localhost/
>
> ![The Bluesky Share button](./images/share_button.webp)

That's the whole point of this article: a small custom React component, swizzled into Docusaurus's `BlogPostItem`, with nothing more exotic than a `<button>` and an SVG icon. Let's build it.

> **Information:**
>
> In a second article (see [Show Bluesky comments on Docusaurus and, too, number of likes/reposts](/blog/docusaurus-bluesky-comments)), we went further: retrieving the number of likes, reposts and comments posted on Bluesky and displaying them below our articles as a single, unified "engagement facepile".

This is what this very button looks like today, live on this blog, for a fresh article that hasn't collected any Bluesky interaction yet:

> Screenshot — http\://localhost/
>
> ![The Share button, live on the blog today](./images/share_button_live.webp)

Because I had absolutely no idea on how to start; let's ask some AI to generate a skeleton. I've asked something like "*Hello my friend, I would like to show a Bluesky share button below my article on my Docusaurus blog. Give me a skeleton; please.*"

Once generated by the AI, it's now time to read, understand and make some tests.

## We need to override how the article is rendered by Docusaurus

So I learned I have to create a file called `src/theme/BlogPostItem/index.js` and just put some content to it.

The AI gives me the content but ... hey my friend, from where comes that content? After some research, I learn it's an override of the standard `@docusaurus/theme-classic` template.

I also learn we can **extract** the original file by using the so-called **Swizzle** action.

> **Information:**
>
> In the Docusaurus terminology, Swizzle can be understood as override: we'll extract, here, a template so we can then override it.

The `src/theme/BlogPostItem/index.js` file will **customize the layout and content of individual blog posts** so, by updating that file, I can override then change how every single post of my blog will look.

Since I want to add a **Share button**; it's the perfect candidate. The AI is right.

## Extract the original file

Start a console and make sure you're in the root folder of your blog.

In your console, run `yarn docusaurus swizzle @docusaurus/theme-classic BlogPostItem`.

When prompted, select `Javascript`, then **`Eject`** and finally `YES`.

![Swizzle the BlogPostItem layout](./images/swizzle_blogpostitem.webp)

As you can see, a lot of files will be created in our Docusaurus directory's structure.

### Keep things under control

As you can see on the screen, you'll extract a lot of files. **Let's keep things under control: we just want to add a share button below our article; we don't want to do more than that.**

Look at your Docusaurus site now; go to the `src/theme/BlogPostItem` folder and see that, yes, you've now a lot of files and sub folders. Just remove every sub folders; we don't want to update them i.e. we want to keep the original ones. So next time you'll install a newer version of Docusaurus, you'll stay up to date.

Just keep the `index.js` file created by the `swizzle` command. Here is the one I just got on my current Docusaurus version:

**src/theme/BlogPostItem/index.js**

```javascript
import React from "react";
import clsx from "clsx";
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
import BlogPostItemContainer from "@theme/BlogPostItem/Container";
import BlogPostItemHeader from "@theme/BlogPostItem/Header";
import BlogPostItemContent from "@theme/BlogPostItem/Content";
import BlogPostItemFooter from "@theme/BlogPostItem/Footer";
// apply a bottom margin in list view
function useContainerClassName() {
  const { isBlogPostPage } = useBlogPost();
  return !isBlogPostPage ? "margin-bottom--xl" : undefined;
}
export default function BlogPostItem({ children, className }) {
  const containerClassName = useContainerClassName();
  return (
    <BlogPostItemContainer className={clsx(containerClassName, className)}>
      <BlogPostItemHeader />
      <BlogPostItemContent>{children}</BlogPostItemContent>
      <BlogPostItemFooter />
    </BlogPostItemContainer>
  );
}
```

## Playing and adding a stupid message to every blog post

Before proceeding, let us conduct a test: is this the correct file to modify for our need?

Let's play and add a line just after our post title. Look at the example below; I've just added a new line:

**src/theme/BlogPostItem/index.js**

```javascript
import React from "react";
import clsx from "clsx";
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
import BlogPostItemContainer from "@theme/BlogPostItem/Container";
import BlogPostItemHeader from "@theme/BlogPostItem/Header";
import BlogPostItemContent from "@theme/BlogPostItem/Content";
import BlogPostItemFooter from "@theme/BlogPostItem/Footer";
// apply a bottom margin in list view
function useContainerClassName() {
  const { isBlogPostPage } = useBlogPost();
  return !isBlogPostPage ? "margin-bottom--xl" : undefined;
}
export default function BlogPostItem({ children, className }) {
  const containerClassName = useContainerClassName();
  return (
    <BlogPostItemContainer className={clsx(containerClassName, className)}>
      <BlogPostItemHeader />
      // highlight-next-line
      {/* Just after the blog post title, we'll add a "Are you ready" text */}
      // highlight-next-line
      <strong style={{ color: "red" }}>
        Are you ready to update your BlogPostItem layout?
      </strong>
      <BlogPostItemContent>{children}</BlogPostItemContent>
      <BlogPostItemFooter />
    </BlogPostItemContainer>
  );
}
```

Save the file and refresh your page using your browser and tadaaa 🎉🎉🎉... Yes, we can see that text on each blog post.

On the image here above, we can see our stupid message:

> Screenshot — http\://localhost/
>
> ![Are you ready?](./images/are_you_ready.webp)

> **Note:**
>
> If it didn't work, please stop and restart your Docusaurus server. On my case (I'm using Docker), I just need to stop and restart my container.

So, as you can see, it's now quite easy. We can change the layout of our Docusaurus page just by manipulating the `index.js` file.

## Time to create our Bluesky Docusaurus component

We'll proceed step by step and keeping in mind a few things:

**Things to Remember**

* We should minimize our changes on the original `src/theme/BlogPostItem/index.js`; the less updates the best;

* We will create a Docusaurus component in `src/components/` to allow interactions with Bluesky (think about the evolution of the component) so, for this reason, we'll put everything (code, image, css) together and

* We will try to keep things clean and maintainable for the developer and configurable for the user.

### 1/5 The layout of our Bluesky generic component

The first file we'll create will be our `index.tsx` file i.e. our global Bluesky component.

In this article, we'll just foresee a "Share on Bluesky" button but in the second article, we'll add some more features.

This `index.tsx` will be the **entry point** of the component.

Please create the `src/components/Bluesky/index.tsx` file with this content:

**src/components/Bluesky/index.tsx**

```typescript
import BlueskyShare from "./share";
import styles from "./styles.module.css";

interface Props {
  metadata: {
    frontMatter?: {
      blueskyRecordKey?: string;
    };
  };
}

export default function Bluesky({ metadata }: Props) {
  return (
    <div className={styles.blueskyContainer}>
      <BlueskyShare metadata={metadata} />
    </div>
  );
}
```

**This is our React `<Bluesky />` component! Simple no? Right now, it's just a div containing another component called `<BlueskyShare />`**.

If you look at the code here above, we have to create two new files: `share.tsx` and `styles.module.css`.

### 2/5 Our "Share on Bluesky" button

Ok, now, we have to create a second file called `src/components/Bluesky/share.tsx` and to copy/paste this content in it:

**src/components/Bluesky/share.tsx**

```typescript
import Icon from "./bluesky.svg";
import styles from "./styles.module.css";
import Translate, { translate } from "@docusaurus/Translate";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";

// Declared locally rather than imported from useBlueskyEngagement — this file (built in the
// series' first article) stands on its own before that hook exists in the tutorial's timeline.
interface Props {
  metadata: {
    title?: string;
    permalink?: string;
    frontMatter?: {
      blueskyRecordKey?: string;
    };
  };
}

export default function BlueskyShare({ metadata }: Props) {
  const { siteConfig } = useDocusaurusContext();
  const blueskyRecordKey = metadata?.frontMatter?.blueskyRecordKey;

  if (blueskyRecordKey) return null;

  if (!metadata.title || !metadata.permalink) {
    console.debug("<BlueskyShare> Missing required properties", { metadata });
    return null;
  }

  const shareLink =
    `https://bsky.app/intent/compose?text=` +
    `${encodeURIComponent(metadata.title)}%20${siteConfig.url}${encodeURIComponent(
      metadata.permalink,
    )}`;

  return (
    <a
      href={shareLink}
      target="_blank"
      rel="noopener noreferrer"
      className={styles.blueskyButton}
      aria-label={translate({
        id: "bluesky.share.ariaLabel",
        message: "Share this post on Bluesky",
      })}
    >
      {/* alt dropped: SVGProps has no such prop — was a no-op DOM attribute even before this
          migration, and the link's own aria-label already names the icon. */}
      <Icon className={styles.blueskyLogo} />
      <Translate id="bluesky.share.label">Share on Bluesky</Translate>
    </a>
  );
}
```

As you can see, we've defined a function called `BlueskyShare` and that function ask a parameter called `metadata`.

> **Information:**
>
> Look back in the `src/components/Bluesky/index.tsx` we've just created before. We had this line: `<BlueskyShare metadata={metadata} />` so, yes, we're well passing that parameter.
>
> The `metadata` parameter is an object initialized by Docusaurus and that will contain all the front matter data from the Markdown file of the current blog post. It includes properties like title, description, date, tags, and any other custom fields you've defined in the front matter.

What does the `BlueskyShare` function? In fact, we'll just provide a button with a url like `https://bsky.app/intent/compose?text=<title>%20<url>` (where we'll inject the blog post title and url).

If you look at the code here above, we have to create another file called: `bluesky.svg`.

### 3/5 The Bluesky logo

For sure, we need the Bluesky logo somewhere.

Please create a third file called `src/components/Bluesky/bluesky.svg` and copy/paste this content in it:

**src/components/Bluesky/bluesky.svg**

```svg
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 600 530" xmlns="http://www.w3.org/2000/svg">
  <path d="m135.72 44.03c66.496 49.921 138.02 151.14 164.28 205.46 26.262-54.316 97.782-155.54 164.28-205.46 47.98-36.021 125.72-63.892 125.72 24.795 0 17.712-10.155 148.79-16.111 170.07-20.703 73.984-96.144 92.854-163.25 81.433 117.3 19.964 147.14 86.092 82.697 152.22-122.39 125.59-175.91-31.511-189.63-71.766-2.514-7.3797-3.6904-10.832-3.7077-7.8964-0.0174-2.9357-1.1937 0.51669-3.7077 7.8964-13.714 40.255-67.233 197.36-189.63 71.766-64.444-66.128-34.605-132.26 82.697-152.22-67.108 11.421-142.55-7.4491-163.25-81.433-5.9562-21.282-16.111-152.36-16.111-170.07 0-88.687 77.742-60.816 125.72-24.795z" fill="#1185fe"/>
</svg>
```

### 4/5 And finally our CSS

And, very important, we need a CSS. Please create a fourth file called `src/components/Bluesky/styles.module.css` and copy/paste this content in it:

**src/components/Bluesky/styles.module.css**

```css
.blueskyLogo {
  width: 20px;
  height: 20px;
}

.blueskyContainer {
  margin: 2rem 0 0.5rem;
  padding: 1.1rem 1.25rem 1.4rem;
  border: 1px solid var(--ifm-color-emphasis-200);
  border-radius: 8px;
  background: var(--ifm-background-surface-color);
}

.blueskyHeadline {
  display: block;
  font-weight: 500;
  color: var(--ifm-color-content);
  margin-bottom: 0.75rem;
}

.blueskyAccountNote {
  display: block;
  margin-top: 0.6rem;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyCommentsContainer {
  border: 1px solid var(--ifm-color-emphasis-200);
  margin-top: 1rem;
  padding: 0.4rem 0.9rem;
  margin-bottom: 1.25rem;
}

.blueskyCommentContainer {
  margin-bottom: 1rem;
  background-color: var(--ifm-color-emphasis-100);
  padding: 0.8rem;
  border-radius: 6px;
  border-left: 2px solid var(--ifm-color-emphasis-300);
  color: var(--ifm-font-color-base);
}

.blueskyCommentHeader {
  display: flex;
  align-items: center;
  gap: 10px;
}

.blueskyCommentAuthorInfos {
  display: flex;
  flex-direction: column;
}

.blueskyCommentAvatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  margin-right: 0.5rem;
}

.blueskyCommentAuthorDisplayName {
  margin: 0;
  font-weight: bold;
}

.blueskyCommentAuthorHandle {
  margin: 0;
  font-size: smaller;
  font-weight: normal;
  color: var(--ifm-color-emphasis-600);
  display: block;
}

.blueskyCommentDate {
  margin-left: auto;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyCommentText {
  margin: 0.3rem 0 0;
}

.blueskyCommentLikes::before {
  content: "❤️\a0";
}

.blueskyCommentLikes::after {
  content: " ";
}

.blueskyCommentReposts::before {
  content: "🔁\a0";
}

.blueskyCommentReposts::after {
  content: " ";
}

.blueskyCommentLink::before {
  content: "→\a0";
}

.blueskyCommentLink::after {
  content: " ";
}

.blueskyCommentLink {
  margin-right: 0.25rem;
  display: inline-flex;
  align-items: center;
  color: var(--ifm-color-emphasis-600);
  text-decoration: none;
  margin-top: 0.25rem;
}

img.blueskyCommentEmbedThumb {
  max-height: 250px;
}

.blueskyCommentImages {
  margin-top: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.blueskyCommentImage {
  max-width: 100%;
  height: auto;
  border-radius: 0.25rem;
  border: 1px solid var(--ifm-color-emphasis-300);
}

.blueskyNoCommentYet {
  color: var(--ifm-color-emphasis-600);
  font-size: 1rem;
  line-height: 1.5;
  text-align: center;
  padding: 20px;
  background-color: var(--ifm-color-emphasis-100);
  border: 1px solid var(--ifm-color-emphasis-300);
  border-radius: 8px;
  margin: 20px 0;
}

.blueskyNoCommentYetCTA {
  font-weight: bold;
  color: var(--ifm-color-primary-dark);
  cursor: pointer;
  text-decoration: underline;
}

[data-theme="dark"] .blueskyNoCommentYetCTA {
  color: var(--ifm-color-primary-light);
}

.blueskyPostLikes {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.blueskyLikersAvatars {
  display: inline-flex;
  align-items: center;
  margin-left: 0.4rem;
}

.blueskyLikersAvatarLink {
  margin-left: -1rem;
  line-height: 0;
}

.blueskyLikersAvatarLink:first-child {
  margin-left: 0;
}

.blueskyLikersAvatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--ifm-background-color);
  display: block;
}

.blueskyLikersAvatarMore {
  margin-left: 0.4rem;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyEngagementRow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.blueskyButton {
  align-items: center;
  background-color: var(--ifm-color-emphasis-200);
  border-radius: 6px;
  border: 1px solid var(--ifm-color-emphasis-300);
  box-shadow: none;
  color: var(--ifm-font-color-base);
  display: inline-flex;
  font-size: 0.9rem;
  font-weight: 400;
  gap: 0.4rem;
  margin-bottom: 0.8rem;
  margin-right: 0.4rem;
  padding: 0.4rem 0.9rem;
  text-decoration: none;
  transition:
    background-color 0.2s ease,
    color 0.2s ease;
}

.blueskyButton:hover {
  background-color: var(--ifm-color-primary);
  border-color: var(--ifm-color-primary);
  color: white;
}
```

### 5/5 Last thing, we need to implement our component in Docusaurus

Our Bluesky component is ready. We have created our four files.

You should have this:

```tree expanded=true showJSX=false debug=false title="src"
.
├── components/Bluesky
│   ├── bluesky.svg
│   ├── index.tsx
│   ├── share.tsx
│   └── styles.module.css
...
└── theme/BlogPostItem
    ├── index.js
```

Let's go back to what was said earlier: in order to adapt the page layout, Docusaurus uses the file `src/theme/BlogPostItem/index.js`. So it's time to inject our newly-created component in that script.

**src/theme/BlogPostItem/index.js**

```javascript
import React from "react";
import clsx from "clsx";
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
import BlogPostItemContainer from "@theme/BlogPostItem/Container";
import BlogPostItemHeader from "@theme/BlogPostItem/Header";
import BlogPostItemContent from "@theme/BlogPostItem/Content";
import BlogPostItemFooter from "@theme/BlogPostItem/Footer";

// highlight-next-line
// import our Bluesky component
// highlight-next-line
import Bluesky from "@site/src/components/Bluesky";

// apply a bottom margin in list view
function useContainerClassName() {
  const { isBlogPostPage } = useBlogPost();
  return !isBlogPostPage ? "margin-bottom--xl" : undefined;
}

export default function BlogPostItem({ children, className }) {
  // highlight-next-line
  // We need to retrieve the isBlogPostPage flag
  // highlight-next-line
  const { metadata, isBlogPostPage } = useBlogPost();
  const containerClassName = useContainerClassName();
  return (
    <BlogPostItemContainer className={clsx(containerClassName, className)}>
      <BlogPostItemHeader />
      <BlogPostItemContent>{children}</BlogPostItemContent>
      <BlogPostItemFooter />
      // highlight-next-line
      {/* Only display Bluesky components on the post page; not the blog view */}
      // highlight-next-line
      {isBlogPostPage && <Bluesky metadata={metadata} />}
    </BlogPostItemContainer>
  );
}
```

Congratulations! If you go to a page on your blog and refresh the page, you will see the Bluesky button at the bottom of the page.

> **Note:**
>
> If it didn't work, please stop and restart your Docusaurus server. On my case (I'm using Docker), I just need to stop and restart my container.

Refresh a blog post page and you'll see the same button we already teased at the top of this article, now live on your own site.

## All Files at a Glance

Here is the full set of files created in this article. Use the button below to copy them all at once, or download them as a ready-to-extract ZIP:

**Project: Bluesky share button**

> **Note:**
>
> Add these files to your existing Docusaurus site, then restart your dev server so the
> swizzled `BlogPostItem` picks up the new component.

**src/theme/BlogPostItem/index.js**

```javascript
import React from "react";
import clsx from "clsx";
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
import BlogPostItemContainer from "@theme/BlogPostItem/Container";
import BlogPostItemHeader from "@theme/BlogPostItem/Header";
import BlogPostItemContent from "@theme/BlogPostItem/Content";
import BlogPostItemFooter from "@theme/BlogPostItem/Footer";

// highlight-next-line
// import our Bluesky component
// highlight-next-line
import Bluesky from "@site/src/components/Bluesky";

// apply a bottom margin in list view
function useContainerClassName() {
  const { isBlogPostPage } = useBlogPost();
  return !isBlogPostPage ? "margin-bottom--xl" : undefined;
}

export default function BlogPostItem({ children, className }) {
  // highlight-next-line
  // We need to retrieve the isBlogPostPage flag
  // highlight-next-line
  const { metadata, isBlogPostPage } = useBlogPost();
  const containerClassName = useContainerClassName();
  return (
    <BlogPostItemContainer className={clsx(containerClassName, className)}>
      <BlogPostItemHeader />
      <BlogPostItemContent>{children}</BlogPostItemContent>
      <BlogPostItemFooter />
      // highlight-next-line
      {/* Only display Bluesky components on the post page; not the blog view */}
      // highlight-next-line
      {isBlogPostPage && <Bluesky metadata={metadata} />}
    </BlogPostItemContainer>
  );
}
```

**src/components/Bluesky/index.tsx**

```typescript
import BlueskyShare from "./share";
import styles from "./styles.module.css";

interface Props {
  metadata: {
    frontMatter?: {
      blueskyRecordKey?: string;
    };
  };
}

export default function Bluesky({ metadata }: Props) {
  return (
    <div className={styles.blueskyContainer}>
      <BlueskyShare metadata={metadata} />
    </div>
  );
}
```

**src/components/Bluesky/share.tsx**

```typescript
import Icon from "./bluesky.svg";
import styles from "./styles.module.css";
import Translate, { translate } from "@docusaurus/Translate";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";

// Declared locally rather than imported from useBlueskyEngagement — this file (built in the
// series' first article) stands on its own before that hook exists in the tutorial's timeline.
interface Props {
  metadata: {
    title?: string;
    permalink?: string;
    frontMatter?: {
      blueskyRecordKey?: string;
    };
  };
}

export default function BlueskyShare({ metadata }: Props) {
  const { siteConfig } = useDocusaurusContext();
  const blueskyRecordKey = metadata?.frontMatter?.blueskyRecordKey;

  if (blueskyRecordKey) return null;

  if (!metadata.title || !metadata.permalink) {
    console.debug("<BlueskyShare> Missing required properties", { metadata });
    return null;
  }

  const shareLink =
    `https://bsky.app/intent/compose?text=` +
    `${encodeURIComponent(metadata.title)}%20${siteConfig.url}${encodeURIComponent(
      metadata.permalink,
    )}`;

  return (
    <a
      href={shareLink}
      target="_blank"
      rel="noopener noreferrer"
      className={styles.blueskyButton}
      aria-label={translate({
        id: "bluesky.share.ariaLabel",
        message: "Share this post on Bluesky",
      })}
    >
      {/* alt dropped: SVGProps has no such prop — was a no-op DOM attribute even before this
          migration, and the link's own aria-label already names the icon. */}
      <Icon className={styles.blueskyLogo} />
      <Translate id="bluesky.share.label">Share on Bluesky</Translate>
    </a>
  );
}
```

**src/components/Bluesky/bluesky.svg**

```svg
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 600 530" xmlns="http://www.w3.org/2000/svg">
  <path d="m135.72 44.03c66.496 49.921 138.02 151.14 164.28 205.46 26.262-54.316 97.782-155.54 164.28-205.46 47.98-36.021 125.72-63.892 125.72 24.795 0 17.712-10.155 148.79-16.111 170.07-20.703 73.984-96.144 92.854-163.25 81.433 117.3 19.964 147.14 86.092 82.697 152.22-122.39 125.59-175.91-31.511-189.63-71.766-2.514-7.3797-3.6904-10.832-3.7077-7.8964-0.0174-2.9357-1.1937 0.51669-3.7077 7.8964-13.714 40.255-67.233 197.36-189.63 71.766-64.444-66.128-34.605-132.26 82.697-152.22-67.108 11.421-142.55-7.4491-163.25-81.433-5.9562-21.282-16.111-152.36-16.111-170.07 0-88.687 77.742-60.816 125.72-24.795z" fill="#1185fe"/>
</svg>
```

**src/components/Bluesky/styles.module.css**

```css
.blueskyLogo {
  width: 20px;
  height: 20px;
}

.blueskyContainer {
  margin: 2rem 0 0.5rem;
  padding: 1.1rem 1.25rem 1.4rem;
  border: 1px solid var(--ifm-color-emphasis-200);
  border-radius: 8px;
  background: var(--ifm-background-surface-color);
}

.blueskyHeadline {
  display: block;
  font-weight: 500;
  color: var(--ifm-color-content);
  margin-bottom: 0.75rem;
}

.blueskyAccountNote {
  display: block;
  margin-top: 0.6rem;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyCommentsContainer {
  border: 1px solid var(--ifm-color-emphasis-200);
  margin-top: 1rem;
  padding: 0.4rem 0.9rem;
  margin-bottom: 1.25rem;
}

.blueskyCommentContainer {
  margin-bottom: 1rem;
  background-color: var(--ifm-color-emphasis-100);
  padding: 0.8rem;
  border-radius: 6px;
  border-left: 2px solid var(--ifm-color-emphasis-300);
  color: var(--ifm-font-color-base);
}

.blueskyCommentHeader {
  display: flex;
  align-items: center;
  gap: 10px;
}

.blueskyCommentAuthorInfos {
  display: flex;
  flex-direction: column;
}

.blueskyCommentAvatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  margin-right: 0.5rem;
}

.blueskyCommentAuthorDisplayName {
  margin: 0;
  font-weight: bold;
}

.blueskyCommentAuthorHandle {
  margin: 0;
  font-size: smaller;
  font-weight: normal;
  color: var(--ifm-color-emphasis-600);
  display: block;
}

.blueskyCommentDate {
  margin-left: auto;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyCommentText {
  margin: 0.3rem 0 0;
}

.blueskyCommentLikes::before {
  content: "❤️\a0";
}

.blueskyCommentLikes::after {
  content: " ";
}

.blueskyCommentReposts::before {
  content: "🔁\a0";
}

.blueskyCommentReposts::after {
  content: " ";
}

.blueskyCommentLink::before {
  content: "→\a0";
}

.blueskyCommentLink::after {
  content: " ";
}

.blueskyCommentLink {
  margin-right: 0.25rem;
  display: inline-flex;
  align-items: center;
  color: var(--ifm-color-emphasis-600);
  text-decoration: none;
  margin-top: 0.25rem;
}

img.blueskyCommentEmbedThumb {
  max-height: 250px;
}

.blueskyCommentImages {
  margin-top: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.blueskyCommentImage {
  max-width: 100%;
  height: auto;
  border-radius: 0.25rem;
  border: 1px solid var(--ifm-color-emphasis-300);
}

.blueskyNoCommentYet {
  color: var(--ifm-color-emphasis-600);
  font-size: 1rem;
  line-height: 1.5;
  text-align: center;
  padding: 20px;
  background-color: var(--ifm-color-emphasis-100);
  border: 1px solid var(--ifm-color-emphasis-300);
  border-radius: 8px;
  margin: 20px 0;
}

.blueskyNoCommentYetCTA {
  font-weight: bold;
  color: var(--ifm-color-primary-dark);
  cursor: pointer;
  text-decoration: underline;
}

[data-theme="dark"] .blueskyNoCommentYetCTA {
  color: var(--ifm-color-primary-light);
}

.blueskyPostLikes {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.blueskyLikersAvatars {
  display: inline-flex;
  align-items: center;
  margin-left: 0.4rem;
}

.blueskyLikersAvatarLink {
  margin-left: -1rem;
  line-height: 0;
}

.blueskyLikersAvatarLink:first-child {
  margin-left: 0;
}

.blueskyLikersAvatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--ifm-background-color);
  display: block;
}

.blueskyLikersAvatarMore {
  margin-left: 0.4rem;
  font-size: smaller;
  color: var(--ifm-color-emphasis-600);
}

.blueskyEngagementRow {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.blueskyButton {
  align-items: center;
  background-color: var(--ifm-color-emphasis-200);
  border-radius: 6px;
  border: 1px solid var(--ifm-color-emphasis-300);
  box-shadow: none;
  color: var(--ifm-font-color-base);
  display: inline-flex;
  font-size: 0.9rem;
  font-weight: 400;
  gap: 0.4rem;
  margin-bottom: 0.8rem;
  margin-right: 0.4rem;
  padding: 0.4rem 0.9rem;
  text-decoration: none;
  transition:
    background-color 0.2s ease,
    color 0.2s ease;
}

.blueskyButton:hover {
  background-color: var(--ifm-color-primary);
  border-color: var(--ifm-color-primary);
  color: white;
}
```

In a next article, we'll learn how to interact with Bluesky:

* Providing a button below the article to jump to the Bluesky posts so your visitors will be able to:
  * Like your article (which is always nice, isn't it?),
  * Share your article (even better) and
  * Start or participate to a discussion
* We'll also show the number of likes and reposts on the Bluesky post and
* Retrieve all comments posted on Bluesky and display them on your blog; this will undoubtedly increase your visitors' engagement.

## Final remarks - Take care about the swizzle command

You need to understand that, by swizzling (overriding) the BlogPostItem layout, you're no more *aligned* with the standard layout of Docusaurus.

**If, in a next release, new features will be added by Docusaurus; you'll not have them!** since you're no more using the default layout for your post.

Just keep that in mind and, perhaps, from time to time (after a major release f.i.), think to run the swizzle command again and restart your customization.

This is why our changes to the index.js file were kept to a minimum.
