Sorry this is unclear!
It is absolutely not required to use system users with JupyterHub in general. System users are a requirement of some spawners (including the default and systemdspawner used in the-littlest-jupyterhub) and some Authenticators (include default and NativeAuth used in the-littlest-jupyterhub). So while TLJH makes choices that require system users, JupyterHub itself does not. TLJH is a “distribution” of JupyterHub that makes a lot of choices for you to simplify deployment, but they are far from the only choices you can make when deploying JupyterHub yourself.
The Authenticator needs some way to identify users. JupyterHub doesn’t care how. Authenticating with external services such as OAuth or LDAP do not need local accounts, while authenticating with the local PAM service does, for example.
The Spawner needs some way to launch servers on behalf of users. Like the Authenticator, JupyterHub doesn’t care how. To a first approximation, this means that container-based spawners (e.g. DockerSpawner or KubeSpawner) do not need system users, but local process spawners (including the default and systemd) do need system users because they launch processes as users using setuid or sudo or some such.
So one common and simple configuration that has no requirement for system users is OAuthenticator + DockerSpawner because the user accounts live somewhere else (GitHub, Google, etc.) and docker containers are used for isolation instead of system user accounts. This is what I typically use for small demos/workshops, etc.