Vercel's scriptc: Building Tiny TypeScript Binaries Without Node
Vercel Labs introduces scriptc, a TypeScript compiler creating native executables without Node.js, promising speed and minimal memory use.
- Topic
- Engineering
- Reading time
- 5 min
- Length
- 1,204 words
- Published
- Sep 25, 2026
09:41 pm IST
In this article
Vercel Labs recently unveiled scriptc, a groundbreaking compiler that transforms TypeScript into native executables, completely bypassing the need for Node.js, V8, or any JavaScript engine in the resulting binary. This development allows for the creation of small, efficient executables that could significantly benefit TypeScript developers aiming to boost performance and reduce memory usage.
What Makes scriptc Different?
The most striking aspect of scriptc is its ability to compile TypeScript directly into a native application. The process starts with the real TypeScript compiler handling parsing and type checking. It then lowers the program to a typed intermediate representation (IR), which can be further compiled into various outputs such as C, LLVM IR, assembly, or even WebAssembly targeting WASI Preview 1. This multi-target capability is particularly useful for developers looking to port applications across different environments without rewriting code.
One of the key features is the tiered compilation strategy scriptc employs. Constructs in the TypeScript code are categorized into three tiers:
- Static Compilation: By default, the code is compiled statically. This means that the majority of the TypeScript code is converted directly into native machine code, potentially resulting in significant performance improvements due to the absence of runtime interpretation. This static compilation is particularly beneficial for applications where performance and memory usage are critical, as it eliminates the overhead associated with runtime environments.
- Dynamic Execution: If the
--dynamicflag is used, some portions of the code, especially those reliant on npm packages or any typed code, are run within an embedded quickjs-ng engine, which is approximately 620KB in size. This allows for dynamic features and runtime flexibility but at the cost of some performance. The embedded engine serves as a fallback for constructs that cannot be efficiently compiled statically, maintaining the ability to execute complex JavaScript features. - Compile-Time Rejection: Some code might be rejected during compilation with specific SC codes, providing hints for rewriting. This is essential for ensuring that the compiled application remains efficient and free from constructs that cannot be efficiently translated into native code. Developers receive feedback on unsupported patterns, enabling them to refactor the code for better performance.
These tiers allow developers to balance between the performance benefits of static compilation and the flexibility of dynamic execution, depending on the specific needs of their applications.
Why This Matters
For developers maintaining production codebases, scriptc offers compelling benefits, especially in terms of startup speed and memory usage. In benchmarks, scriptc showed a median CLI startup time of 1.78ms, significantly outperforming Bun's 21.29ms and Node's 61.78ms. Moreover, the idle memory usage of a framework-free node:http server compiled with scriptc was just 1.9MiB, which is a substantial reduction compared to typical Node.js applications.
For developers focused on performance-sensitive applications, these metrics highlight the potential for scriptc to reduce resource consumption and improve application responsiveness. The ability to generate a single, small executable without runtime dependencies simplifies deployment and reduces the attack surface for security vulnerabilities.
Implementing scriptc in Your Projects
To get started with scriptc, ensure your development environment is running Node.js 24 or newer, as this is a prerequisite. You can install scriptc via npm:
npm install -g scriptc
Before diving into converting existing projects, it is crucial to understand scriptc's limitations and deliberate divergences. This includes:
- Strings are stored as UTF-8, which may affect how string manipulation is handled, especially in projects that rely heavily on string processing. This can lead to differences in behavior compared to the typical UTF-16 encoding used in JavaScript environments.
- Memory management is done through reference counting instead of garbage collection. This can lead to differences in memory handling and potential memory leaks if circular references are not properly managed. Developers need to be vigilant about memory usage patterns, particularly in applications with complex object graphs.
Object.keys()reports keys in declaration order, which differs from the ECMAScript standard where keys are ordered based on insertion. This can impact applications that rely on specific key ordering for object iteration.process.argv[0]is set to "scriptc", which might affect scripts that rely on this value for process identification. Any logic that checks the process name needs to be adjusted to account for this change.
For projects heavily reliant on npm packages, the --npm-static flag is available to attempt compiling these dependencies out of the dynamic execution "island". This is particularly useful for reducing the footprint of applications that require third-party libraries, though success may vary based on the complexity and nature of the packages used. Developers should test this feature thoroughly to ensure compatibility with their dependencies.
Limitations and Considerations
While scriptc presents many advantages, it also comes with its limitations. Notably, the reliance on the QuickJS engine for dynamically executed code can lead to performance bottlenecks. For instance, in tests involving the Hono framework, about 62% of the server code required dynamic execution, significantly reducing throughput to 18.4k requests per second compared to Bun's 70.5k. This illustrates the trade-off between maintaining JavaScript functionality and achieving optimal performance.
Another notable limitation is the early stage of the project. Coverage reports can generate numerous errors, indicating that scriptc might not yet be suitable for all existing projects, particularly those relying heavily on third-party libraries. Developers might find themselves considering alternative languages like Rust or Go, which are natively compiled and have matured ecosystems. The experimental status of scriptc means that it is currently best suited for projects where the benefits of native compilation outweigh the potential challenges.
Additionally, concerns about the longevity of the project have been raised, referencing Vercel's previous project, zerolang, which saw limited post-launch development. This makes scriptc an option better suited for experimentation and specific use cases, rather than a wholesale replacement for Node.js in production environments. Developers should be cautious about relying on scriptc for long-term projects until it demonstrates sustained development and support.
In terms of compatibility, scriptc targets macOS, Linux, Windows, and WASI Preview 1, providing a wide range of platforms for deployment. However, the experimental nature of the project means that developers should be prepared for potential changes and updates that could affect their projects. Keeping abreast of updates and changes to scriptc is crucial for maintaining compatibility and leveraging new features as they become available.
For developers interested in using scriptc, it is advisable to start with small, contained projects to evaluate its capabilities and limitations. Experimenting with non-critical applications can provide valuable insights into how scriptc handles specific code patterns and dependencies. This approach allows developers to gradually build confidence in scriptc's capabilities before considering it for larger, more complex projects.
In my experience, it's essential to thoroughly test the compiled executables across all intended platforms to ensure consistent behavior. Pay particular attention to edge cases and scenarios involving dynamic execution, as these are more likely to encounter issues due to the reliance on QuickJS. This testing phase is critical for identifying and addressing potential issues early in the development cycle.
Overall, scriptc offers a novel approach to compiling TypeScript into native executables, with the potential to significantly impact performance and resource usage. However, its experimental status and certain limitations mean it should be approached with caution, particularly for mission-critical applications. Developers should weigh the benefits of faster startup times and lower memory usage against the challenges of integration and long-term support.
Sources
Vercel Labs Ships scriptc, a TypeScript-to-Native Compiler That Leaves the JavaScript Engine Behind
Every claim above was checked against this source before publishing. The analysis, the code and the opinions are mine.
Frequently asked
What is scriptc?
scriptc is a compiler from Vercel Labs that converts TypeScript into native executables, eliminating the need for Node.js or any JavaScript engine in the binary.
How does scriptc improve performance?
scriptc offers faster startup times and lower memory usage compared to Node.js, with benchmarks showing significant improvements in both areas.
What are scriptc's limitations?
scriptc can generate errors with coverage on existing projects, relies on an embedded QuickJS engine for dynamic code, and is still experimental, making it less suitable for all use cases.
How do I install scriptc?
You need Node.js 24 or newer, and can install scriptc using npm with the command: npm install -g scriptc.