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.
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