You already met fixtures as the things in the brackets: async ({ page }) => {}. That page is a fixture Playwright provides. Now you write your own, and this is where Playwright stops being a test runner and starts being a framework.
Here is the problem. Half your tests need a logged-in user. A quarter need a contact that already exists. You could put that in beforeEach, but then every test in the file pays for setup it may not need, and the test cannot say what it requires.
A custom fixture is setup that a test asks for by name.