How to replace captured groups only?

A solution is to add captures for the preceding and following text:

str.replace(/(.*name="\w+)(\d+)(\w+".*)/, "$1!NEW_ID!$3")

Now that Javascript has lookbehind (as of ES2018), on newer environments, you can avoid groups entirely in situations like these. Rather, lookbehind for what comes before the group you were capturing, and lookahead for what comes after, and replace with just !NEW_ID!:

const str = 'name="some_text_0_some_text"';
console.log(
  str.replace(/(?<=name="\w+)\d+(?=\w+")/, '!NEW_ID!')
);

With this method, the full match is only the part that needs to be replaced.

  • (?<=name="\w+) - Lookbehind for name=", followed by word characters (luckily, lookbehinds do not have to be fixed width in Javascript!)
  • \d+ - Match one or more digits - the only part of the pattern not in a lookaround, the only part of the string that will be in the resulting match
  • (?=\w+") - Lookahead for word characters followed by " `

Keep in mind that lookbehind is pretty new. It works in modern versions of V8 (including Chrome, Opera, and Node), but not in most other environments, at least not yet. So while you can reliably use lookbehind in Node and in your own browser (if it runs on a modern version of V8), it's not yet sufficiently supported by random clients (like on a public website).


A little improvement to Matthew's answer could be a lookahead instead of the last capturing group:

.replace(/(\w+)(\d+)(?=\w+)/, "$1!NEW_ID!");

Or you could split on the decimal and join with your new id like this:

.split(/\d+/).join("!NEW_ID!");

Example/Benchmark here: https://codepen.io/jogai/full/oyNXBX