Skip to content
Laravel Badge

Laravel Badge · Guide

Making Your Laravel Package Discoverable on Packagist

Most composer.json files only have enough metadata to install. Here's what keywords, homepage, support, and funding actually do for discovery.

Author:
Peter Fox
Published:
Length:
7 min read

The guide

Section 01

Composer reads your composer.json to install a package. Packagist reads it to decide what to show a stranger who's never heard of your package and is trying to work out, in about four seconds, whether it's worth a closer look. Most maintainers optimise heavily for the first audience and barely touch the second.

The result is a Packagist page with a name, a version number, and not much else — no tags to click through from a related package, no link to documentation, no way to tell if the maintainer is still around. None of that is a bug in Packagist. It's metadata that was never filled in.

This guide covers the composer.json fields that exist specifically for that second audience, what each one actually renders as once it's published, and a complete example you can adapt.


What Packagist actually does with your metadata

A package page on Packagist is assembled almost entirely from fields you control:

  • Title and description — your package name and the description string, shown at the top of the page and in search results.
  • Keywords — rendered as clickable tags. Clicking one runs a Packagist search for that term, which is also how people land on your package while looking for something else entirely.
  • Homepage — a direct link, separate from the repository link.
  • Support linksissues, docs, source, chat, email, and the others, each shown as its own labelled link rather than buried in a README.
  • Funding — sponsorship links, shown as a distinct "fund this package" element when present.

Packagist's search index refreshes every five minutes after a package is crawled, so there's no lag-based excuse for shipping a tag or a doc link and not seeing it appear. If a field is empty, it's because composer.json doesn't have it — not because Packagist is slow to notice.

keywords: the field everyone forgets

keywords takes an array of strings and turns each one into a clickable tag on your package page. It's also one of the few fields that feeds Packagist's search and category filtering directly, which means a missing keywords array isn't a cosmetic gap — it's a way for someone searching for exactly what your package does to never see it.

{
    "keywords": [
        "laravel",
        "php",
        "rate-limiting",
        "middleware",
        "throttle"
    ]
}

A useful keyword list mixes the broad terms people start a search with (laravel, php) with the specific terms that describe the actual problem you solve (rate-limiting, feature-flags, audit-log). The broad terms get you into a category; the specific ones get you found by someone who already knows what they're looking for. Composer restricts keyword characters to letters, numbers, spaces, dots, underscores, and dashes, so keep each entry to a single short term rather than a comma-separated phrase.

homepage and support: telling people where to actually go

homepage is a single URL — your project's front door if you have one separate from the repository: a documentation site, a landing page, anything more deliberately built than a GitHub README.

support is where most of the useful links live, and it's a full object rather than a single string:

{
    "support": {
        "docs": "https://example.com/docs",
        "issues": "https://github.com/vendor/package/issues",
        "source": "https://github.com/vendor/package",
        "chat": "https://discord.gg/example",
        "security": "https://github.com/vendor/package/security/policy"
    }
}

Each key Packagist understands — email, issues, forum, wiki, irc, source, docs, rss, chat, security — shows up as its own labelled link on the package page. If your repository host already provides a "source" link by virtue of being a GitHub repo, the field worth adding deliberately is docs: a lot of packages have real documentation living on a separate site that never gets connected back to the Packagist listing at all.

support.security: give vulnerability reports a destination

support.security deserves calling out on its own, because it answers a question that otherwise has no good default: where does someone send a vulnerability report instead of opening a public GitHub issue?

{
    "support": {
        "security": "https://github.com/vendor/package/security/policy"
    }
}

This pairs directly with filing advisories through FriendsOfPHP or the GitHub Advisory Database, covered in Using Composer 2.10 to Help Your Users Stay Secure — having a support.security link doesn't replace filing the advisory once a fix ships, but it gives a researcher somewhere responsible to go before that point exists. The same instinct applies to the account behind the package; see Securing Your Packagist Account for the other half of that picture.

funding: letting adoption turn into support

funding is an array of { "type": ..., "url": ... } pairs. type is just a label — Packagist recognises common platforms like github, patreon, tidelift, and open_collective, and falls back to a generic link for other or anything it doesn't specifically recognise.

{
    "funding": [
        {
            "type": "github",
            "url": "https://github.com/sponsors/vendor"
        },
        {
            "type": "open_collective",
            "url": "https://opencollective.com/vendor"
        }
    ]
}

It's a small field, but it's the one most directly tied to whether downloads ever turn into anything sustaining the maintainer. If you're already tracking adoption numbers — see Measuring the Success of Your Laravel Packagefunding is the field that gives a fraction of those users a way to act on it.

abandoned: discoverability includes knowing when to stop looking

It feels backwards to include a field for "this package shouldn't be found," but it belongs here precisely because it's a discovery problem in reverse. A package with no commits in three years and no abandoned flag still shows up in search results looking exactly as viable as one that's actively maintained — someone has to install it and dig through issues before they find out otherwise.

{
    "abandoned": "vendor/the-actively-maintained-fork"
}

Setting abandoned to true flags the package without a recommendation; pointing it at a string names the replacement directly, and Packagist surfaces that suggestion right on the package page. It costs nothing and saves the next person the hour you already know they'll lose.

A complete example

Pulling the discovery-facing fields into one block:

{
    "name": "vendor/package",
    "description": "A short, specific sentence describing what this package does.",
    "keywords": ["laravel", "php", "rate-limiting", "middleware"],
    "homepage": "https://example.com",
    "support": {
        "docs": "https://example.com/docs",
        "issues": "https://github.com/vendor/package/issues",
        "source": "https://github.com/vendor/package",
        "security": "https://github.com/vendor/package/security/policy"
    },
    "funding": [
        {
            "type": "github",
            "url": "https://github.com/sponsors/vendor"
        }
    ]
}

None of this is required for composer install to work. All of it is required for a stranger on Packagist to understand, in the few seconds they'll actually spend, what your package does, where to read more, and whether anyone is still home.

The checklist

  1. Write a description that's one specific sentence, not a restatement of the package name.
  2. Add 4–8 keywords — a couple of broad terms, the rest specific to the problem you solve.
  3. Set homepage if you have anything more deliberate than the bare repository.
  4. Fill in support.docs and support.security at minimum — the two links a Packagist page is least likely to have by default.
  5. Add funding if you accept sponsorship anywhere, even informally.
  6. Mark abandoned the day you know you've stopped maintaining a package, ideally pointing at whatever you'd recommend instead.

For more on what happens once people find the package — measuring whether it's actually being adopted, and keeping the account behind it secure — see Measuring the Success of Your Laravel Package and Securing Your Packagist Account.

End of guide · 2026

← Back to the archive

Put a badge on your package

Section 02 · Make it yours

Dynamic SVG badges, generated from Packagist in seconds.

Generate a badge →