How to create Dynamic Routes
DynamicRoutesAlter.php

Problem #1 - preg_replace() without pattern

$url = preg_replace('_', '-', $key);


The $url will be always NULL, because you are using preg_replace() without regexp, use instead str_replace() or write appropriate regexp pattern: '/_/'.

Solution #1

$url = preg_replace('/_/', '-', $key);


Solution #2

$url = str_replace('_', '-', $key);


Problem #2 - wrong variable mixing

$collection-&gt;add('custom_module.dynamic_routes'.{$key}, $route);


You are wrong with mixing variable and string. You want to concatenate it with the curly syntax. This is also incorrect.

Solution #1

$collection-&gt;add("custom_module.dynamic_routes.{$key}", $route);


Solution #2

$collection-&gt;add('custom_module.dynamic_routes' . $key, $route);


After you fix those problems, your code will work.

I'm also not sure about 'type' =&gt; $key,, why is that here? There is nothing about type route param.

P.s. as you mentioned itself, better use slugs.

UPD - all files

Check your files for mistakes.

custom_module.info.yml

name: Custom module
type: module
description: For https://drupal.stackexchange.com/questions/281728
package: Custom
core: 8.x


custom_module.routing.yml

custom_module.dynamic_routes:
  path: '/custom-module/dynamic-routes'
  defaults:
    _title: 'Dynamic Routes'
    _controller: '\Drupal\custom_module\Controller\DynamicRoutesController::productType'
  requirements:
    _permission: 'access content'


custom_module.services.yml

services:
  custom_module.dynamic_routes:
    class: Drupal\custom_module\Routing\DynamicRoutesAlter
    tags:
      - { name: event_subscriber }


src/Controller/DynamicRoutesController.php

&lt;?php

namespace Drupal\custom_module\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * Returns responses for Custom module routes.
 */
class DynamicRoutesController extends ControllerBase {

  /**
   * Builds response for dynamic routes.
   */
  public function productType() {

    $build['results'] = [
      '#markup' =&gt; $this-&gt;t('Controller to get Products List.'),
    ];

    return $build;
  }

}


src/Routing/DynamicRoutesAlter.php

&lt;?php

namespace Drupal\custom_module\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

/**
 * DynamicRoutesAlter Class.
 */
class DynamicRoutesAlter extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  public function alterRoutes(RouteCollection $collection) {

    $products = ['cooking_gear' =&gt; 'Cooking Gear', 'tents' =&gt; 'Tents' , 'sleeping_bags' =&gt; 'Sleeping Bags','rope' =&gt; 'Rope', 'safety' =&gt; 'Safety','packs' =&gt; 'Packs'];


    foreach ($products as $key =&gt; $value) {

      $url = preg_replace('/_/', '-', $key);

      $route = new Route(
      // The url path to match.
        '/custom-module/dynamic-routes/' . $url,
        // The defaults (see the custom_module.dynamic_routes for structure)
        [
          '_title' =&gt; $value,
          '_controller' =&gt; '\Drupal\custom_module\Controller\DynamicRoutesController::productType',
          'type' =&gt; $key,
        ],
        // The requirements.
        [
          '_permission' =&gt; 'create ' . $value,
        ]
      );

      // Add our route to the collection with a unique key.
      $collection-&gt;add('custom_module.dynamic_routes' . $key, $route);

    }
  }

}