Bun 03 ⚡ Initializing Projects and package.json Integration
Bun’s project initialization is designed to eliminate the configuration burden that Node.js projects typically carry. Running bun init generates a complete, working project with sensible defaults, and it integrates with package.json in ways that differ from npm and Yarn: it writes a typecheck script, it understands workspaces natively, and it defaults to treating package scripts as first-class commands. The package.json that Bun produces is still a standard Node.js manifest, but the integration layer—how Bun reads, writes, and executes it—is optimized for the runtime.
Key point: bun init is non-destructive and infers settings from the directory. It creates an entry point, a TypeScript configuration, and a package.json with a typecheck script, then runs bun install to install @types/bun and typescript. The resulting manifest works with Node.js and npm, but Bun reads it differently.
Why Bun’s initialization differs
The zero-config problem. A Node.js project requires a package.json, a tsconfig.json (for TypeScript), an entry point, and a test configuration. Each file has its own schema and its own documentation. bun init generates all of them from a single interactive prompt. The tsconfig.json is preconfigured for Bun’s features: moduleResolution: "bundler", allowImportingTsExtensions, and types: ["bun"] . A developer can start writing TypeScript immediately without editing configuration files.
The entry point inference problem. bun init does not arbitrarily choose index.ts. It checks for existing entry points (index.tsx, index.jsx, index.js, index.mts, index.mjs) and uses the one it finds. If none exist, it defaults to index.ts. If package.json already specifies a module or main field, that entry point is used . This means running bun init in an existing project does not break the existing structure—it fills in the missing pieces.
The script execution problem. In npm, running npm run dev spawns a subshell, locates the script in package.json, and executes it. In Bun, bun dev is the equivalent of bun run dev unless dev conflicts with a built-in Bun command. Bun’s startup time for scripts is approximately 6ms, compared to npm’s 170ms . The package.json scripts are the same; the execution engine is different.
The workspace problem. Bun’s package.json integration extends to workspaces. The workspaces field in the root manifest lists the glob patterns for sub-packages. Bun supports full glob syntax, including negative patterns (!packages/**/test/**), and it deduplicates dependencies across workspaces during bun install . The workspace protocol (workspace:*) is replaced with the actual version during publishing, just as pnpm does.
The trade-off. Bun’s defaults are optimized for Bun. A package.json created by bun init includes a typecheck script that runs bun check, which is a Bun-specific command. The tsconfig.json includes "types": ["bun"], which requires @types/bun. Projects that need to run under Node.js must ensure the configuration does not depend on Bun-only features.
a. Running bun init and understanding the output
bun init is interactive by default. It prompts for a project template (Blank, React, or Library) and then generates the files. The -y flag accepts the defaults without prompting .
# Interactive
bun init
# Accept defaults
bun init -y
# Initialize in a specific directory
bun init my-app
The files created depend on the template and the existing directory contents. For a blank template in an empty directory, Bun creates:
package.jsonwith a name derived from the directory and atypecheckscripttsconfig.jsonwith Bun’s recommended compiler optionsindex.tsas the entry pointREADME.md.gitignore
It then runs bun install to install @types/bun and typescript as dev dependencies .
The bun init command is non-destructive. Running it in an existing project does not overwrite files that already exist. It fills in the missing configuration files and updates package.json only where necessary .
b. The package.json that bun init produces
The generated package.json is minimal but functional. It includes name, version, type: "module", and a typecheck script that invokes bun check. The type: "module" field makes the project an ES module by default, matching the direction of modern Node.js and Bun’s native ESM support.
{
"name": "my-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"typecheck": "bun check"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "^5.0.0"
}
}
The typecheck script is a Bun-specific addition. bun check runs the TypeScript compiler in check-only mode, using the tsconfig.json that bun init generated. This script is what makes the package.json “Bun-aware” while remaining compatible with npm and Node.js—the script is just a string, and the dependencies are standard npm packages.
For React projects, bun init --react generates a package.json with react and react-dom dependencies, and a tsconfig.json configured for JSX ("jsx": "react-jsx"). The --react=tailwind and --react=shadcn presets add the corresponding dependencies and configuration .
c. Integrating package.json with Bun’s runtime
Bun reads package.json fields that npm and Yarn ignore, and it interprets some fields differently. The scripts field is the most important. bun run <script> executes the script in a subshell, respecting pre and post hooks. If predev exists, it runs before dev; if it fails, dev does not run .
Bun also respects the trustedDependencies field in package.json. Unlike npm and Yarn, Bun does not execute lifecycle scripts (postinstall, preinstall) by default. A package that needs to run a script must be listed in trustedDependencies. This is a security measure: it prevents arbitrary code execution during installation .
{
"trustedDependencies": ["esbuild", "sharp"]
}
The workspaces field is fully supported. Bun’s workspace resolution deduplicates dependencies and links workspace packages. The workspace:* protocol is replaced with the actual version during bun publish, matching the behavior of pnpm and Yarn Berry .
The overrides and resolutions fields are also read. overrides is npm’s mechanism for pinning transitive dependencies; resolutions is Yarn’s. Bun supports both, preferring overrides when both exist . This allows projects migrating from npm or Yarn to keep their existing override rules without modification.
Complete Example Session
# ============================================
# PART 1: INITIALIZE A PROJECT
# ============================================
mkdir my-bun-app && cd my-bun-app
bun init -y
# Creates package.json, tsconfig.json, index.ts, README.md
# ============================================
# PART 2: INSPECT PACKAGE.JSON
# ============================================
cat package.json
# {
# "name": "my-bun-app",
# "version": "1.0.0",
# "type": "module",
# "scripts": { "typecheck": "bun check" },
# "devDependencies": { ... }
# }
# ============================================
# PART 3: INSPECT TSCONFIG
# ============================================
cat tsconfig.json
# Includes "types": ["bun"], "moduleResolution": "bundler"
# ============================================
# PART 4: RUN THE ENTRY POINT
# ============================================
bun run index.ts
# Executes TypeScript without transpilation.
# ============================================
# PART 5: ADD A SCRIPT
# ============================================
# Edit package.json:
# "scripts": { "dev": "bun run index.ts" }
bun dev
# Bun runs the script (or bun run dev).
# ============================================
# PART 6: TRUST A NATIVE PACKAGE
# ============================================
# Add to package.json:
# "trustedDependencies": ["esbuild"]
bun add esbuild
# postinstall script runs because esbuild is trusted.
# ============================================
# PART 7: CONFIGURE WORKSPACES
# ============================================
# Root package.json:
# "workspaces": ["packages/*"]
bun install
# Dependencies for all workspaces are installed.
# ============================================
# PART 8: USE OVERRIDES
# ============================================
# package.json:
# "overrides": { "bar": "4.4.0" }
bun install
# Transitive dependency bar is pinned to 4.4.0.
# ============================================
# PART 9: TYPECHECK
# ============================================
bun run typecheck
# Runs bun check, reports TypeScript errors.
The nine parts trace initialization, configuration inspection, entry point execution, script addition, native dependency trust, workspace setup, overrides, and type checking. Each step modifies package.json and observes Bun’s interpretation of it.
Quick Reference
bun init Flags
| Flag | Effect |
|---|---|
-y / --yes | Accept defaults without prompting |
--react | Scaffold a React project |
--react=tailwind | React + Tailwind CSS |
--react=shadcn | React + shadcn/ui + Tailwind |
Generated Files
| File | Purpose |
|---|---|
package.json | Manifest with typecheck script |
tsconfig.json | Bun-recommended compiler options |
index.ts | Default entry point |
README.md | Project readme |
.gitignore | Ignore patterns |
package.json Fields Bun Reads
| Field | Behavior |
|---|---|
scripts | Run with bun run or bun <script> |
trustedDependencies | Allow lifecycle scripts |
workspaces | Monorepo support with glob |
overrides | Pin transitive dependencies |
resolutions | Yarn-compatible overrides |
Best Practices
✅ Do This:
bun init -y
<!-- Non-interactive initialization --> # ✅
{ "trustedDependencies": ["esbuild"] }
<!-- Explicitly trust native packages --> # ✅
bun run typecheck
<!-- Type check before committing --> # ✅
❌ Don’t Do This:
# Assume lifecycle scripts run by default
<!-- Bun blocks them for security --> # ❌
# Edit tsconfig.json without understanding Bun's defaults
<!-- The generated file is already configured --> # ❌
# Run bun init in a non-empty directory without checking
<!-- It is non-destructive, but review the changes --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Native module fails | Lifecycle script blocked | Add to trustedDependencies |
Type errors on Bun global | @types/bun not installed | bun add -d @types/bun |
bun dev runs built-in instead of script | Name collision | Use bun run dev |
| Workspace dependency not found | Not declared in workspace manifest | Add with workspace:* |
| TypeScript 6+ types not found | types array missing "bun" | Add "types": ["bun"] to tsconfig |
Real-World Examples
1. Initialize
bun init -y
2. React Scaffold
bun init --react=tailwind
3. Run Script
bun dev
4. Trust Package
{ "trustedDependencies": ["sharp"] }
5. Workspace Root
{ "workspaces": ["packages/*"] }
6. Override
{ "overrides": { "lodash": "4.17.21" } }
7. Typecheck
bun check
8. Add Types
bun add -d @types/bun
9. Entry Point Inference
# Bun uses index.tsx if it exists
10. Inspect Manifest
cat package.json
Visual
bun init Flow
┌──────────────────────────────────────────────┐
│ bun init │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Detect existing files │
│ ├─ index.tsx? → use as entry │
│ ├─ index.js? → use as entry │
│ └─ none → create index.ts │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Generate package.json, tsconfig.json │
│ README.md, .gitignore │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ bun install (typescript, @types/bun) │
└──────────────────────────────────────────────┘
package.json Interpretation
┌──────────────────────────────────────────────┐
│ package.json │
│ │
│ scripts → bun run <script> │
│ trustedDependencies → allow postinstall │
│ workspaces → monorepo linking │
│ overrides → pin transitive deps │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Bun reads fields that npm ignores │
│ (trustedDependencies, workspace protocol) │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Init command | bun init |
| Non-interactive | -y / --yes |
| Default entry | index.ts (or existing) |
| Generated tsconfig | Bun-recommended options |
| Generated script | "typecheck": "bun check" |
| Lifecycle scripts | Blocked unless in trustedDependencies |
| Workspaces | workspaces field with glob |
| Overrides | overrides or resolutions |
Key takeaways:
bun initis non-destructive and infers entry points. It checks for existing files and uses them rather than overwriting. The result is a working project with minimal configuration .- The generated
package.jsonis Bun-aware but Node.js-compatible. Thetypecheckscript andtrustedDependenciesfield are Bun-specific, but the file remains a standard npm manifest. - Lifecycle scripts are blocked by default. Packages that need
postinstallmust be listed intrustedDependencies. This is a security measure that changes the migration workflow for native modules . - Workspaces and overrides are first-class. Bun reads
workspaceswith full glob support and honors both npmoverridesand Yarnresolutions. - TypeScript 6+ requires explicit type configuration. The
tsconfig.jsonmust include"types": ["bun"]for theBunglobal to be recognized .
Remember: bun init is not just a scaffolding command. It configures the package.json for Bun’s interpretation: scripts run faster, lifecycle scripts are gated by trust, workspaces are deduplicated, and overrides are respected. The manifest remains portable, but the integration layer is Bun-specific.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!