You can reuse GitHub Actions workflows to avoid duplicating logic across multiple repositories or even within the same repository.

Here’s a workflow that defines a reusable workflow:

# .github/workflows/reusable-workflow.yml
name: Reusable CI

on:
  workflow_call:
    inputs:
      node-version:
        description: 'The Node.js version to use'
        required: true
        type: string
        default: '18.x'
    secrets:
      npm-token:
        description: 'NPM token for publishing'
        required: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:

          node-version: ${{ inputs.node-version }}

          cache: 'npm'
      - name: Install dependencies
        run: npm ci
      - name: Run tests
        run: npm test
      - name: Publish to NPM

        if: ${{ inputs.npm-token }}

        run: npm publish
        env:

          NODE_AUTH_TOKEN: ${{ secrets.npm-token }}

This workflow is designed to be called by another workflow. The on: workflow_call: section defines the entry point for this reusable workflow. It specifies inputs and secrets that can be passed in when the workflow is called. In this case, it accepts a node-version input and an optional npm-token secret.

The jobs within this reusable workflow are standard. The setup-node action uses the inputs.node-version to configure Node.js. The npm publish step conditionally runs only if an npm-token is provided and uses that token from the secrets.npm-token input.

Now, here’s how you would call this reusable workflow from another workflow:

# .github/workflows/main-workflow.yml
name: Main Workflow

on:
  push:
    branches:
      - main

jobs:
  call-reusable-workflow:
    uses: './.github/workflows/reusable-workflow.yml' # Path to the reusable workflow
    with:
      node-version: '20.x'
    secrets:

      npm-token: ${{ secrets.NPM_TOKEN }} # Pass your NPM token from repository secrets

This main-workflow.yml demonstrates calling the reusable workflow. The uses: key specifies the path to the reusable workflow file. The with: key passes the required inputs, such as node-version: '20.x'. If the reusable workflow requires secrets, you can pass them using the secrets: key, referencing your repository’s secrets (e.g., secrets.NPM_TOKEN).

You can also call reusable workflows from different repositories. For example, to use a workflow located in the main branch of the octocat/SPOOKY-ACTION repository:

# .github/workflows/another-repo-workflow.yml
name: Another Repo Workflow

on:
  pull_request:
    branches:
      - main

jobs:
  call-remote-workflow:
    uses: octocat/SPOOKY-ACTION/.github/workflows/reusable-workflow.yml@main
    with:
      node-version: '16.x'

In this example, uses: octocat/SPOOKY-ACTION/.github/workflows/reusable-workflow.yml@main points to the reusable workflow in another repository. The @main specifies the ref (branch, tag, or commit SHA) from which to use the workflow.

The most surprising thing about reusable workflows is that they execute as if they were defined directly in the calling repository’s context, meaning they have access to the same repository secrets and permissions as the calling workflow, unless explicitly restricted. This can be a powerful feature for centralizing common logic while maintaining a familiar execution environment.

The next concept to explore is how to manage inputs and outputs between reusable workflows and their callers, allowing for more complex data flow and orchestration.

Want structured learning?

Take the full Github-actions course →