Here's one demo suggestion: add_action( 'admin_enqueue_scripts', function() { wp_enqueue_script( 'my-script', '/my-script.js', ['underscore', 'backbone'], '1.0' ); wp_add_inline_script( 'my-script', 'alert("hello world");' ); // Add our template if( function_exists( 'wpse_add_inline_tmpl' ) ) wpse_add_inline_tmpl( $handle = 'my-script', $id = 'my-tmpl', $tmpl = '<div class="section intro">{{{ data.section.intro }}}</div>' ); } ); where we define our custom `wpse_add_inline_tmpl()` function as: function wpse_add_inline_tmpl( $handle, $id, $tmpl ) { // Collect input data static $data = []; $data[$handle][$id] = $tmpl; // Append template for relevant script handle add_filter( 'script_loader_tag', function( $tag, $hndl, $src ) use ( &$data, $handle, $id ) { // Nothing to do if no match if( ! isset( $data[$hndl][$id] ) ) return $tag; // Script tag replacement aka wp_add_inline_script() if ( false !== stripos( $data[$hndl][$id], '</script>' ) ) $data[$hndl][$id] = trim( preg_replace( '#<script[^>]*>(.*)</script>#is', '$1', $data[$hndl][$id] ) ); // Append template $tag .= sprintf( "<script type='text/template' id='%s'>\n%s\n</script>" . PHP_EOL, esc_attr( $id ), $data[$hndl][$id] ); return $tag; }, 10, 3 ); } Here we use the `script_loader_tag` filter to inject the template code and use both the *handle* and *id* to reference it. We also use the *script* tag [replacement][1] from the core `wp_add_inline_script()` function. Hope you can test this further and modify to your needs. Writing this as a class might be a better approach ;-) [1]: https://github.com/WordPress/WordPress/blob/f645178391426ce859aad0bebf461516e1165123/wp-includes/functions.wp-scripts.php#L116