Documentation

Move Strings Into Array

Reference guides for release workflows, command-line usage, cross-file protections, and the desktop app.

Inside the Docs

Practical guides for real release work.

How-to guides Start with release sequencing and command-line usage, then move into feature-specific references.
Advanced protection Browse cross-file controls like Replace Globals and Protect Members when a build spans multiple scripts.

Move Strings Into Array

  • MoveStrings
  • Corporate

Move Strings Into Array lifts the string literals out of your statements and puts them in one generated table near the start of the file. Every original use site becomes an index into that table. The strings still exist — but they no longer sit beside the logic that explains what they are for.

What it does

Instead of keeping every string directly beside the code that uses it, the obfuscator stores those strings in a shared table and reads them indirectly. That breaks the visual connection between a statement and the data it depends on. The table order is generated, so index 0 is not the first string in your source.

Readable source:

o.style.color = "red";

After strings are moved into a table, especially when combined with Move Members:

o[a[23]][a[52]] = a[145];

Three separate things became table entries there: the property name style, the property name color, and the value "red". Only the last of those was a string literal in your source — the other two are the work of Move Members, described below.

Why it helps

Most fast reconnaissance on a JavaScript bundle is string search. An analyst greps for /api/, for an error message they saw in the console, for license, for a feature-flag name — and lands directly on the function that matters. Moving strings into a table breaks that shortcut. The grep still finds the literal, but it finds it in a flat table at the top of the file with no surrounding context, and the site that uses it is an anonymous index somewhere else in the output.

Be clear about the boundary: this is not encryption. The strings are plaintext in the output, and a reader who follows an index gets exactly the text you started with. What you have bought is that following the index is a manual step per string instead of a free one.

Move Members depends on this option

Move Members (MoveMembers) rewrites dotted property access into bracket access — obj.name becomes obj["name"] — which turns every property name in the file into an ordinary string literal. Those literals are then swept into the table by this option.

That ordering is why MoveMembers only takes effect when MoveStrings is also enabled. Turned on alone it would replace readable dotted access with equally readable bracket access and hide nothing. Enable them together, which is what every preset that uses either one does.

Layering with string encoding

This option is the foundation the string-hiding features build on. They compose in a fixed order:

OptionEffect on the tableRequires
MoveStrings (this page) Literals collected into one indexed table, still plaintext. Corporate
EncodeStrings Table entries are respelled so the plaintext no longer appears literally in the file. Free
EncryptStrings Table entries are recovered through generated runtime logic rather than a fixed respelling. Enterprise

Enabling MoveStrings alone is the cheapest useful step and costs almost nothing at runtime. Adding encoding on top is what removes the plaintext, at the cost of work per string read.

EncodeStrings is the one member of this family available on the Free tier, and it works without the table — it respells literals in place. If you are on Free and want string protection, that is the option to reach for; the table itself needs Corporate. See plans.

Configuration

{
  "options": {
    "MoveStrings": true,
    "MoveMembers": true,
    "EncodeStrings": true
  }
}

Migrating from the open-source javascript-obfuscator package? Its stringArray option maps to MoveStrings, and stringArrayThreshold enables MoveStrings when greater than zero. stringArrayEncoding maps to EncodeStrings or EncryptStrings. splitStrings and splitStringsChunkLength map directly to bounded native literal splitting (1-1024 characters; default 10). The full table is on the npm migration page.

Strings you must keep verbatim

Some literals cannot move. A string another system matches on — a CSS selector your stylesheet also declares, a message a partner service parses, a magic value in a protocol — has to survive byte-for-byte. Use reservedStrings for these: literals matching one of the patterns stay verbatim and are left out of both MoveStrings and EncodeStrings.

{
  "reservedStrings": ["^data-", "^/health$"],
  "options": { "MoveStrings": true, "EncodeStrings": true }
}

Up to 100 patterns are accepted, each at most 512 characters. Keep the list short and specific — a broad pattern such as . quietly opts your whole file out of string protection while the build still reports success.

Caveats worth knowing before you ship

  • Output grows. Every use site becomes a table reference and the table itself is added. Raw size goes up; gzipped size goes up much less, because repeated table identifiers compress well. We measured this across 42 libraries in how much bigger does obfuscation make your bundle.
  • Bracket access defeats some tooling. With Move Members on, console.log becomes a bracket read, so bundlers, tree shakers, and analyzers that look for dotted member access no longer see it. Run protection after your bundler, never before.
  • Private names are skipped. Members the engine identifies as private names are left as dotted access rather than being rewritten, so #field-style access keeps working.
  • Optional chaining is preserved. obj?.member becomes obj?.["member"], not the invalid obj?["member"]. This was a real regression fixed in May 2026; if you see a parse error mentioning a semicolon where a colon was required, you are on an older engine.
  • Directive prologues are protected. A leading "use strict" is a directive, not an expression, and moving it into the table would silently drop strict mode. The engine detects the directive prologue and leaves it alone — the full story is in does obfuscation preserve use strict.

Frequently asked questions

What does moving strings into an array actually change?

It lifts string literals out of the statements that use them and puts them in one generated table near the top of the file, leaving each original use site as an index into that table. The strings still exist and are still readable, but they no longer sit next to the logic that explains their purpose. The table order is generated rather than source order, so the first entry is not the first string you wrote.

Is this the same as encrypting strings?

No, and the documentation is explicit about the boundary. The strings are plaintext in the output, and anyone who follows an index arrives at exactly the text you started with. What the option buys is that following the index is a manual step for each string instead of something a reader gets for free. String encoding and encryption are separate options that layer on top of this one.

What is the point if a search still finds the text?

Because what the search returns is different. Fast reconnaissance on a bundle is usually a search for an API path, an error message seen in the console, a word like licence, or a feature-flag name, and the hit normally lands the analyst directly on the function that matters. With the strings in a table, the same search lands in a flat list at the top of the file with no surrounding context, and the code that uses it is an anonymous index elsewhere in the output.

Why does moving members require this option to be enabled?

Because the two work in sequence. Moving members rewrites dotted property access into bracket access, which turns every property name in the file into an ordinary string literal. Those new literals are then swept into the table by this option. Enabled on its own, moving members would only replace readable dotted access with equally readable bracket access and would hide nothing, which is why it takes effect only when string moving is on as well. Every preset that uses either one enables both.

Which plan includes this option?

It requires a Corporate plan, and its API parameter is the string-moving option name shown at the top of this page. The member-moving option that depends on it sits in the same tier, so enabling the pair does not require a further upgrade.

What is the most common way this breaks a build?

A string that something outside your code depends on gets treated like any other literal. Property names read by a server response, keys used by an external library, and anything looked up by name across a boundary are the usual cases. The mechanical problems are caught by compatibility validation, but a reserved string you forgot to reserve is only caught by running your own tests against the protected bundle and watching for undefined-property errors.

Verify, do not assume: string transforms are the most common source of "worked before protection, broken after". After enabling this option, run your test suite against the protected bundle and watch for undefined-property errors. Compatibility validation catches the mechanical cases; only your own tests catch a reserved string you forgot to reserve.

Try this in the online obfuscator

Paste your own code and see this option applied, or compare plans for larger projects and the desktop app.

Try It Free See Pricing