Type-safe PostHog feature flags with automated sync and local development overrides.
- 🔄 Bi-directional sync - Generate TypeScript from JSON, sync to PostHog
- 🎯 Type safety - Full TypeScript support with generated types
- đź”§ Local development - Override flags locally without touching PostHog
- ⚡ GitHub Action - Automated CI/CD integration
curl -fsSL https://raw.githubusercontent.com/stephenbyrne99/hogsync/main/install.sh | bash
Method | Platform | Command |
---|---|---|
Shell Script | Linux, macOS | curl -fsSL https://raw.githubusercontent.com/stephenbyrne99/hogsync/main/install.sh | bash |
npm | All platforms | npm install -g hogsync |
Homebrew | macOS | brew install stephenbyrne99/tap/hogsync (coming soon) |
Scoop | Windows | scoop install hogsync (coming soon) |
Arch AUR | Linux | yay -S hogsync-bin (coming soon) |
Download the latest binary from GitHub Releases:
- Linux x64:
hogsync-linux-x64.zip
- Linux ARM64:
hogsync-linux-arm64.zip
- macOS x64:
hogsync-darwin-x64.zip
- macOS ARM64:
hogsync-darwin-arm64.zip
Note: Windows binaries are not currently available in automated releases. Windows users can install via npm:
npm install -g hogsync
# Direct execution (recommended for CI/build scripts)
npx hogsync generate
bunx hogsync generate
# After global installation
npm install -g hogsync
hogsync generate # Binary is available globally
# As project dependency
npm install --save-dev hogsync
# Then in package.json scripts:
"scripts": {
"generate:flags": "hogsync generate"
}
npx hogsync init
This creates a hogsync.config.js
file:
module.exports = {
// Directory containing feature flag JSON files
flagsDir: 'feature-flags',
// Output file for generated TypeScript
outputFile: 'src/generated/feature-flags.ts',
// PostHog configuration
posthog: {
host: process.env.POSTHOG_HOST || 'https://app.posthog.com',
projectId: process.env.POSTHOG_PROJECT_ID,
apiToken: process.env.POSTHOG_API_TOKEN,
},
// Generation options
generation: {
includeLocalConfigs: true,
namingConvention: 'snake_case', // 'camelCase', 'snake_case', 'SCREAMING_SNAKE_CASE'
}
};
Create JSON files in your feature-flags/
directory:
// feature-flags/dark-mode.flag.json
{
"key": "dark-mode",
"name": "Dark Mode Toggle",
"active": true,
"description": "Enable dark mode theme",
"filters": {
"groups": [
{
"properties": [
{
"key": "email",
"operator": "icontains",
"value": "@company.com"
}
]
}
]
}
}
npx hogsync generate
This generates type-safe constants:
// src/generated/feature-flags.ts
export const FeatureFlags = {
dark_mode: 'dark-mode',
ai_chat: 'ai-chat',
} as const;
export type FeatureFlag = typeof FeatureFlags[keyof typeof FeatureFlags];
export const LocalFeatureFlags = {
dark_mode: {
key: 'dark-mode',
name: 'Dark Mode Toggle',
enabled: true,
},
} as const;
# Set environment variables
export POSTHOG_PROJECT_ID="your-project-id"
export POSTHOG_API_TOKEN="your-api-token"
# Sync flags to PostHog
npx hogsync sync
First, install the React package:
npm install @stephen_byrne_/hogsync-react
The createFeatureFlags
function automatically detects your framework and environment:
import { useFeatureFlagEnabled as usePostHogFeatureFlagEnabled } from "posthog-js/react";
import { LocalFeatureFlags } from "./generated/feature-flags"; // Generated by hogsync
import { createFeatureFlags } from "@stephen_byrne_/hogsync-react";
const {
useFeatureFlagEnabled,
getLocalFeatureFlagConfig,
} = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags);
export function MyComponent() {
const isDarkModeEnabled = useFeatureFlagEnabled('dark-mode');
const isNewFeatureEnabled = useFeatureFlagEnabled('new-feature');
return (
<div className={isDarkModeEnabled ? 'dark' : 'light'}>
{isNewFeatureEnabled && <NewFeatureComponent />}
</div>
);
}
Supported frameworks (auto-detected):
- Next.js - Detects
process.env.NODE_ENV
andNEXT_PUBLIC_NODE_ENV
- Vite - Detects
import.meta.env.DEV
andimport.meta.env.MODE
- Create React App - Detects
process.env.NODE_ENV
- Browser environments - Falls back to hostname-based detection
For custom environments or testing:
const {
useFeatureFlagEnabled,
getLocalFeatureFlagConfig,
} = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
isDevelopment: window.location.hostname.includes('staging'),
debug: false, // Disable debug logging
});
⚡ Fast Setup: The GitHub Action now downloads pre-built binaries instead of compiling from source, making it ~10× faster with no dependency installation required.
Create .github/workflows/feature-flags.yml
:
name: Sync Feature Flags
on:
push:
branches: [main, develop]
paths: ['feature-flags/**']
pull_request:
branches: [main]
paths: ['feature-flags/**']
jobs:
sync-flags:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Sync PostHog Feature Flags
uses: stephenbyrne99/hogsync@v1
with:
posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}
name: Feature Flags CI/CD
on:
push:
branches: [main]
paths: ['feature-flags/**']
jobs:
validate-and-sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Validate flags first
- name: Validate Feature Flags
uses: stephenbyrne99/hogsync@v1
with:
posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}
generate-only: 'true'
flags-dir: 'custom-flags'
output-file: 'src/types/feature-flags.ts'
# Commit generated types
- name: Commit generated types
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add src/types/feature-flags.ts
git diff --staged --quiet || git commit -m "Update feature flag types"
git push
# Sync to PostHog only on main branch
- name: Sync to PostHog
if: github.ref == 'refs/heads/main'
uses: stephenbyrne99/hogsync@v1
with:
posthog-project-id: ${{ secrets.POSTHOG_PROJECT_ID }}
posthog-api-token: ${{ secrets.POSTHOG_API_TOKEN }}
posthog-host: ${{ secrets.POSTHOG_HOST }}
sync-only: 'true'
Input | Description | Required | Default |
---|---|---|---|
version |
HogSync version to use | ❌ | latest |
posthog-project-id |
PostHog project ID | âś… | - |
posthog-api-token |
PostHog API token | âś… | - |
posthog-host |
PostHog host URL | ❌ | https://app.posthog.com |
flags-dir |
Directory containing flag JSON files | ❌ | feature-flags |
output-file |
Output file for TypeScript | ❌ | src/generated/feature-flags.ts |
config-file |
Path to configuration file | ❌ | hogsync.config.js |
sync-only |
Only sync, skip generation | ❌ | false |
generate-only |
Only generate, skip sync | ❌ | false |
Set these in your repository settings:
POSTHOG_PROJECT_ID
- Your PostHog project IDPOSTHOG_API_TOKEN
- Your PostHog API tokenPOSTHOG_HOST
- (Optional) Custom PostHog host
# Initialize configuration
npx hogsync init
# Generate TypeScript from flags
npx hogsync generate
# Sync flags to PostHog
npx hogsync sync
# Validate flag definitions
npx hogsync validate
# Watch for changes (coming soon)
npx hogsync watch
# Show help
npx hogsync --help
# Show version
npx hogsync --version
In development, feature flags use your local JSON configurations:
// In development: uses LocalFeatureFlags (from JSON)
// In production: uses PostHog API
const isEnabled = useFeatureFlagEnabled('new-feature');
The hook automatically detects your environment and shows debug information:
[Feature Flag Debug] dark-mode: {
framework: 'next.js',
isDevelopment: true,
flagKey: 'dark_mode',
localConfig: { key: 'dark-mode', name: 'Dark Mode', enabled: true },
enabled: true,
posthogEnabled: undefined
}
Framework Detection:
vite
- Detected viaimport.meta.env
next.js
- Detected viaNEXT_PUBLIC_NODE_ENV
create-react-app
- Detected viaREACT_APP_*
env varsbrowser
- Fallback for browser environmentsunknown
- When framework cannot be determined
Add to your package.json
:
{
"scripts": {
"flags:generate": "npx hogsync generate",
"flags:sync": "npx hogsync sync",
"flags:validate": "npx hogsync validate",
"dev": "npm run flags:generate && next dev"
}
}
For build environments that support bunx:
{
"scripts": {
"build": "bunx hogsync generate && next build",
"dev": "bunx hogsync generate && next dev"
}
}
For automatic flag generation during development, set up your scripts to regenerate flags before starting your dev server:
{
"scripts": {
"dev": "bun run flags:generate && next dev --port 3001",
"build": "bun run flags:generate && next build",
"flags:generate": "npx hogsync generate",
"flags:sync": "npx hogsync sync",
"flags:validate": "npx hogsync validate",
"start": "next start --port 3001",
"lint": "next lint",
"check-types": "tsc --noEmit",
"format": "biome format --write ."
}
}
This ensures your TypeScript types are always up-to-date when you start development or build your project.
Pre-commit hook to validate flags:
#!/bin/sh
# .git/hooks/pre-commit
npx hogsync validate
// hogsync.config.js
module.exports = {
generation: {
namingConvention: 'camelCase', // darkMode instead of dark_mode
}
};
module.exports = {
environments: {
development: {
posthog: { projectId: 'dev-project' }
},
production: {
posthog: { projectId: 'prod-project' }
}
}
};
// Override auto-detection for specific environments
const hooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
isDevelopment: window.location.hostname.includes('staging'),
debug: false, // Disable logging
});
// Force development mode for testing
const testHooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
isDevelopment: true, // Always use local flags
debug: false
});
Use kebab-case for flag keys:
{
"key": "new-checkout-flow",
"name": "New Checkout Flow"
}
Use PostHog's percentage rollouts:
{
"key": "beta-feature",
"active": true,
"filters": {
"groups": [
{
"rollout_percentage": 10
}
]
}
}
Remove flags after full rollout:
- Set flag to 100% rollout
- Remove conditional code
- Delete flag JSON file
- Run
npx hogsync generate
Use manual overrides in tests:
// Always use local flags in tests
const testHooks = createFeatureFlags(mockPostHogHook, LocalFeatureFlags, {
isDevelopment: true, // Force local flags
debug: false // Disable logging
});
If you have existing PostHog feature flags:
- Export flags from PostHog
- Create JSON files matching the structure
- Run
npx hogsync-cli generate
- Update your code to use the new hooks
If auto-detection fails, manually specify:
// Manual override for edge cases
const hooks = createFeatureFlags(usePostHogFeatureFlagEnabled, LocalFeatureFlags, {
isDevelopment: process.env.NODE_ENV === 'development'
});
Ensure the React package is installed separately:
// This works
import { LocalFeatureFlags } from "./generated/feature-flags";
import { createFeatureFlags } from "@stephen_byrne_/hogsync-react";
// This might cause issues in non-React projects
import { createFeatureFlags } from "hogsync"; // Don't do this
I have no association or affiliation with PostHog, and was just using this internally to manage my own feature flags.
MIT - see LICENSE file for details.