Build a dynamic block using the default attributes

I also had this problem, and what I did is to define the attribute inside PHP and use wp_localize_script to pass down the array.

Then you can pass in the variable in render_callback using use ($var)

For example:

$js = get_stylesheet_directory_uri() . '/assets/js';
$css= get_stylesheet_directory_uri() . '/assets/css';

$default_attributes = [
  'alignment' => [ 'type' => 'string', 'default' => 'left' ],
  'iconPosition' => [ 'type' => 'string', 'default' => 'left' ],

  'textColor' => [ 'type' => 'string', 'default' => 'var(--text)' ],
  'bgColor' => [ 'type' => 'string', 'default' => 'var(--textInvert)' ],
];


wp_register_script( 'my-block', $dist . '/my-block.js', [ 'wp-blocks', 'wp-dom' ] , null, true );
wp_register_style( 'my-block', $dist . '/my-block.css', [ 'wp-edit-blocks' ] );

wp_localize_script( 'my-block', 'myBlockLocalize', [ 'attributes' => $default_attributes ] );

register_block_type( 'my/block', [
  'editor_style' => 'my-block',
  'editor_script' => 'my-block',
  'render_callback' => function( $atts, $inner_blocks = null ) use ( $default_attributes ) {
      $default_values = array_map( function( $a ) {
        return $a['default'] ?? '';
      }, $default_attributes );

      $atts = wp_parse_args( $atts, $default_values );

      ob_start();
      // output the HTML here, I believe you can use get_template_parts()
      return ob_get_clean();
    }
] );

In your JS:

...

registerBlockType( 'my/block', {
  title: 'My Block',
  icon: 'id',
  category: 'layout',
  example: {},
  attributes: myBlockLocalize.attributes,
  
  // ...

} );

...