Talo logoDocsBlogGitHub
Back to blog

How to create a custom Unity package - and why you shouldn't

5 min read
How to create a custom Unity package - and why you shouldn't

TL;DR

Unity's packages allow you to ship custom code and assets that can be easily imported into other projects. However, dependency management can become very tricky for package maintainers.

Why does this blog post exist?

Talo - our open source game backend, publishes a Unity package to allow other game developers to add leaderboards, stats and saves to their games. Over the last few months the Talo package has started encountering many of Unity's package manager limitations.

Sadly, documentation for Unity custom packages is few and far between. Most of the answers you need are buried in Unity discussions rather than official documentation. This post attempts to demistify Unity packages and the pitfalls you need to be aware of.

What is a custom Unity package?

A Unity package is a collection of assets, scripts and other resources that can be easily shared between Unity projects [1]. Usually it's a module that adds specific functionality to your game - it could be anything from a simple utility script to a complete game framework (like Talo).

Unity packages come in two main flavours:

  1. Built-in packages - These are officially maintained by Unity and sometimes come pre-installed with the Unity Editor. Examples include the Unity Test Framework or TextMeshPro.

  2. Custom packages - These are created by developers and can be distributed through various channels including git repositories, local folders or tarballs.

Custom packages are particularly useful when you want to:

  • Distribute your tool or framework to other projects and developers
  • Keep functionality modular and maintainable
  • Version control specific features independently

Custom packages can be easily imported into projects using the Unity Package Manager, accessed via Window > Package Manager.

The structure of a custom package

A custom package is a folder (typically under Packages/com.your-company.your-package) with a package.json file. The package.json describes your custom package including its name, version, description and dependencies.

Here's a example of a package.json file (pulled from the Talo Unity package):

{
  "name": "com.trytalo.talo",
  "version": "0.35.1",
  "displayName": "Talo Game Services",
  "description": "Talo (https://trytalo.com) is an open-source game backend with services designed to help you build games faster. You can currently:\n\n- Identify and authenticate players\n- Store persistent data across players\n- Track events (levelling up, finding loot, etc)\n- Display high scores with leaderboards\n- Store and load player saves\n- Load game config options and flags from the cloud\n- Get feedback directly from your players\n- Send channel messages between players\n- See if players are online and set custom statuses",
  "unity": "6000.0",
  "keywords": [],
  "author": {
    "name": "Talo",
    "email": "help@trytalo.com",
    "url": "https://trytalo.com"
  },
  "type": "library",
  "samples": [
    {
      "displayName": "Playground",
      "description": "A simple UI-based playground, allowing you to test identifying, events, stats and leaderboards",
      "path": "Samples~/Playground"
    },
    {
      "displayName": "Saves Demo",
      "description": "A basic saves UI, allowing you to load, create and update saves",
      "path": "Samples~/SavesDemo"
    },
    {
      "displayName": "Leaderboards Demo",
      "description": "A minimal leaderboard UI, allowing you to add and update entries",
      "path": "Samples~/LeaderboardsDemo"
    },
    {
      "displayName": "Authentication Demo",
      "description": "A register/login/verify flow, showing how to create and authenticate player accounts",
      "path": "Samples~/AuthenticationDemo"
    },
    {
      "displayName": "Chat Demo",
      "description": "A simple chat UI, allowing you to create channels, join channels and send messages",
      "path": "Samples~/ChatDemo"
    }
  ],
  "changelogUrl": "https://github.com/TaloDev/unity/releases",
  "documentationUrl": "https://docs.trytalo.com",
  "dependencies": {
    "com.unity.test-framework": "1.3.0",
    "com.mikeschweitzer.websocket": "3.0.1"
  }
}

name and version are required fields, while displayName, description and unity are optional but strongly recommended [2].

Runtime code vs. Tests

Unity packages can contain both runtime code (i.e. the functional code that will be used in games) and tests. Not only are tests recommended to ensure the package works as expected, but they're also a great way to showcase the package's functionality.

Your runtime code should be placed under the Runtime folder while your tests should be placed under the Tests folder. If you're interested, check out our guide on how to write Unity Test Framework tests to learn how to write your own unit tests.

Understanding scoped registries

Another important concept of the package manager is scoped registries. Scoped registries add extra flexibility by allowing you to add additional package sources beyond Unity's default registry. While Unity's built-in registry hosts official packages, scoped registries let you easily access third-party packages from other sources.

If you're familiar with the JavaScript ecosystem you know that NPM is the default registry for all packages, however you can also add custom scoped registries from sources like private GitHub repositories.

A great example of this is OpenUPM, a registry that hosts thousands of open-source Unity packages. OpenUPM makes it easy to:

  • Discover community-created packages
  • Get specific versions of packages
  • Update packages when new versions are released

To add a scoped registry like OpenUPM to your project, you can modify the manifest.json file in your project's Packages folder or add it through the Project Settings window. Here's what a scoped registry looks like in the manifest.json file:

{
  "dependencies": {
    ...
  },
  "scopedRegistries": [
    {
      "name": "package.openupm.com",
      "url": "https://package.openupm.com",
      "scopes": [
        "com.mikeschweitzer.websocket"
      ]
    }
  ]
}

Custom package dependencies

Like other package ecosystems, Unity's packages can have dependencies. For example, the Talo package depends on the com.unity.test-framework package as shown above. Sadly, dependency management is where everything falls apart.

Package dependency limitations

There are two critical limitations that all package maintainers should be aware of. While Unity suggests their package manager making Unity more open and extensible, custom packages cannot easily depend on packages that are not from the official Unity registry.

In practice this means that you cannot:

  1. Depend on a package from a scoped registry like OpenUPM
  2. Depend on a package from a git repository [3]

If you're relying on an external package, you'll need to advise your users to install it manually as a prerequisite.

Alternatively, you can ask your users to add the scoped registry to their project manually or include the package as part of your custom package. Neither of these solutions are ideal, as they both add unnecessary steps for package maintainers and end users.

There’s no automatic resolution, no "just works" experience. If a user forgets to install something, your package will fail to install.

Unity's Asset Store vs Custom packages

The Unity Asset Store is the primary distribution channel for custom assets, libraries and frameworks. Despite this, you can't just upload a custom package to the Asset Store. You can try to do this using the hybrid-packages project, which has partial official support from Unity.

Unlike the Asset Store, custom packages have close to zero discoverability. In fact, when I polled Reddit users about their custom package vs Asset Store preferences, the top comment was "First time hearing about UPM."

Conclusion

Custom Unity packages can be a powerful way to modularise and distribute code across projects. They provide clean versioning, easy imports and a structured approach to package management.

However, the hidden pitfalls — particularly dependency limitations, lack of discoverability and complex setup requirements — can turn them into a maintenance headache. Unity’s package manager is simply not as flexible as other package managers like NPM, making it difficult to manage third-party dependencies or distribute packages seamlessly.

So, should you create a custom Unity package?

✅ Yes — if you're building a lightweight, self-contained package with minimal dependencies.

⚠️ Maybe — if you’re okay with manual workarounds for missing features.

❌ No — if you need deep integration with external libraries or want broad adoption by other developers.

For Talo, we're reconsidering whether a Unity Asset Store package might be a more scalable solution, avoiding the frustrating limitations of UPM. If you're developing your own Unity package, carefully weigh the trade-offs before committing.

Resources


TudorWritten by Tudor

Build your game faster with Talo

Don't reinvent the wheel. Integrate leaderboards, stats, event tracking and more in minutes.

Using Talo, you can view and manage your players directly from the dashboard. It's free!

Get started

More from the Talo Blog

Changelog: leaderboard refreshes and player presence
2 min read

Changelog: leaderboard refreshes and player presence

This month we've added support for daily leaderboard refreshes and a new API for player presence tracking.

Introducing the Talo Presence API: real-time player tracking made easy
4 min read

Introducing the Talo Presence API: real-time player tracking made easy

Learn how to implement real-time player presence tracking in your game with Talo's new Presence API. Easily track online status, custom states and build social features.

Level up your Unity game with Talo Leaderboards
3 min read

Level up your Unity game with Talo Leaderboards

From daily leaderboards to custom properties and filtering, Talo provides the most comprehensive leaderboards for Unity games.

Introducing Talo Channels: real-time messaging for Godot games
5 min read

Introducing Talo Channels: real-time messaging for Godot games

Learn how to implement real-time messaging in your Godot game using Talo Channels. Build chat rooms, notifications and interactive features with just a few lines of code.