Mastodon feed for your website

Unlike most social media platforms, displaying your latest mastodon posts is a doddle with vanilla JavaScript

Demo

woah! since when does alt+clicking a component name in projects open the component file instead of components.d.ts?? Our biggest dx gripe since Nuxt v1 looks vanquished!

Great work @nuxt 🎉

one more side project domain name released back into the wild. so long, mastodon-id dot party, and welcome mastodon-id.blackspike.com – a simple tool for finding your mastodon id. you could probably have guessed that.

screenshot of netlify build times, first in 4m 51s, next was 46m 8s and the next 4m 52s

updated an image-heavy @astro website to 5.10 and turned on the new responsive images.

They weren't kidding about the increased @netlify
build times 😮 from 5 mins to 45 mins!

Looks like they are cached but damn that took a while

screenshot of a ppt slide with all the design tools in the red header

Should you get tasked with designing a deck, install the super handy and free Toolbar – it places all the design tools right up in the toolbar.

It's by Slidor, a french company cursed to walk the earth designing in PowerPoint for sins unknown

slidor.agency/toolbar-powerpoi

Delighted to finally add our logo to the eduspots.org partner section.

We've just rebuilt their backend and server – it had picked up some digitial barnacles in the 8 or so years since we first designed and built the website.

A brilliant initiative we are proud to support.

1. Get your Mastodon ID

First you’ll need your Mastodon ID

https://example.social/api/v1/accounts/lookup?acct=your-username

In our case https://mastodon.cloud/api/v1/accounts/lookup?acct=blackspike returns the ID 109461933672061521

2. Fetch the posts

Then you’ll need to make a simple fetch call

const postsUrl = "https://mastodon.cloud/api/v1/accounts/109461933672061521/statuses"

const postsFetch = await fetch(postsUrl)

const posts = await fetchPosts.json()
That will return a JSON array like this
[
  {
    "id": "110299745197176137",
    "created_at": "2023-05-02T15:15:45.611Z",
    "in_reply_to_id": null,
    "in_reply_to_account_id": null,
    "sensitive": false,
    "spoiler_text": "",
    "visibility": "public",
    "language": "en",
    "uri": "https://mastodon.cloud/users/blackspike/statuses/110299745197176137",
    "url": "https://mastodon.cloud/@blackspike/110299745197176137",
    "replies_count": 0,
    "reblogs_count": 0,
    "favourites_count": 0,
    "edited_at": null,
    "favourited": false,
    "reblogged": false,
    "muted": false,
    "bookmarked": false,
    "pinned": true,
    "content": "\u003cp\u003eWe are delighted to push our new website live – we welcome shares \u0026amp; feedback 🖤\u003c/p\u003e\u003cp\u003e\u003ca href=\"https://www.blackspike.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003eblackspike.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e",
    "filtered": [],
    "reblog": null,
    "application": null,
    "account": {
      "id": "109461933672061521",
      "username": "blackspike",
      "acct": "blackspike",
      "display_name": "blackspike design",
      "locked": false,
      "bot": false,
      "discoverable": true,
      "group": false,
      "created_at": "2022-12-05T00:00:00.000Z",
      "note": "\u003cp\u003eA freelance full-stack creative development team working remotely from Brighton, UK\u003c/p\u003e",
      "url": "https://mastodon.cloud/@blackspike",
      "avatar": "https://media.mastodon.cloud/accounts/avatars/109/461/933/672/061/521/original/eb1f3ebaecf23fce.png",
      "avatar_static": "https://media.mastodon.cloud/accounts/avatars/109/461/933/672/061/521/original/eb1f3ebaecf23fce.png",
      "header": "https://media.mastodon.cloud/accounts/headers/109/461/933/672/061/521/original/53e72b9acfdbc9e7.jpg",
      "header_static": "https://media.mastodon.cloud/accounts/headers/109/461/933/672/061/521/original/53e72b9acfdbc9e7.jpg",
      "followers_count": 5,
      "following_count": 4,
      "statuses_count": 16,
      "last_status_at": "2023-05-02",
      "noindex": false,
      "emojis": [],
      "roles": [],
      "fields": [
        {
          "name": "www",
          "value": "\u003ca href=\"https://blackspike.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003eblackspike.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
          "verified_at": "2023-04-13T12:30:23.994+00:00"
        },
        {
          "name": "Twitter",
          "value": "\u003ca href=\"https://twitter.com/blackspikeltd\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003etwitter.com/blackspikeltd\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
          "verified_at": null
        },
        {
          "name": "new website coming soon!",
          "value": "\u003ca href=\"https://beta.blackspike.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003ebeta.blackspike.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
          "verified_at": "2023-04-13T12:30:51.474+00:00"
        }
      ]
    },
    "media_attachments": [
      {
        "id": "110299741526912479",
        "type": "gifv",
        "url": "https://media.mastodon.cloud/media_attachments/files/110/299/741/526/912/479/original/492f846154047751.mp4",
        "preview_url": "https://media.mastodon.cloud/media_attachments/files/110/299/741/526/912/479/small/492f846154047751.png",
        "remote_url": null,
        "preview_remote_url": null,
        "text_url": null,
        "meta": {
          "original": {
            "width": 960,
            "height": 960,
            "frame_rate": "60/1",
            "duration": 14.167,
            "bitrate": 6587363
          },
          "small": {
            "width": 400,
            "height": 400,
            "size": "400x400",
            "aspect": 1.0
          },
          "focus": { "x": 0.0, "y": 0.0 }
        },
        "description": "a screen recording of the homepage of blackspike.com being scrolled",
        "blurhash": "U58WaQpvI.$+{[EkNds+8zE0x]kDF-xct7Sc"
      }
    ],
    "mentions": [],
    "tags": [],
    "emojis": [],
    "card": {
      "url": "https://www.blackspike.com/",
      "title": "blackspike design - creative web development",
      "description": "blackspike design is a freelance full-stack creative development team working remotely from Brighton, UK",
      "type": "link",
      "author_name": "",
      "author_url": "",
      "provider_name": "",
      "provider_url": "",
      "html": "",
      "width": 400,
      "height": 210,
      "image": "https://media.mastodon.cloud/cache/preview_cards/images/026/387/228/original/f69a0a79d5a5f7ec.jpg",
      "embed_url": "",
      "blurhash": "UGBeH^}Z=eA?[Ca0nOK3-BS#$is.=Lf,,?w^"
    },
    "poll": null
  }
]

You can see in that example post there is a video and thumbnail image in the media_attachments that we use in our demo carousel.

3. Create HTML from the returned JSON

Use a template literal to create the HTML list items and format the date with toLocaleDateString

const postsList = document.querySelector('.posts')

const postsUrl = 'https://mastodon.cloud/api/v1/accounts/109461933672061521/statuses'

const getPosts = async () => {

  const fetchPosts = await fetch(postsUrl)

  const posts = await fetchPosts.json()

  const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' }

  const markup = posts.map(toot => `
      <li class='toot'>
        <div class='content'>${toot.content}</div>
        <footer>
            ${new Date(toot.created_at).toLocaleDateString('en-GB', dateOptions)}
        </footer>
      </li>
      `
    )
    .join('')

  postsList.innerHTML = markup
}

getPosts()

Add a dash of CSS and you’re done

Of course you can add links, show images, play videos and hide replies etc, but it’s fantastic that you don’t have to worry about OAUTH or API keys or paying billionaires for the privilege of displaying your own content 🤌

See the Pen latest toots! by blackspike (@blackspike) on CodePen.


If you need help with adding a custom feed to your site, do get in touch!