If I feel the customization may be useful for others, then I file a pull request against the upstream project. I strongly think that upstreaming your change is a good practice, and that it should be considered the default.
If the upstream maintainers reject my change, or if I ended up not proposing it upstream for some reason, then I usually do one of the following:
1. I fork the original package and publish it under a new name or in another namespace with my customizations included. Then I keep track of upstream releases by subscribing to a release feed of the original package (e.g. if they use GitHub releases, I append `.atom` to the URL of the GitHub release page). I then port my customization forward on upstream releases. Or,
2. if it’s a Javascript package, I use `yarn patch`, see [1]. Whenever I bump the dependency, I bump/port the patch, too. Or,
3. if all else fails, I think I might vendor the library into my source tree as a last resort. But I’ve never actually done that.