Configuration

You can pass a — what we call — universal configuration object to fine-tune your API reference.

Universal ConfigurationCopied!

It is universal, because it works in all environments. You can pass it to the JS API directly, or you can use it in one of our integrations.

Let’s say you are working with just an HTML file, that’s how you pass the configuration:

Scalar.createApiReference('#app', {
  // Your configuration goes here…
  url: '…'
})

Or — just as an example — in the Hono server framework you would pass the same configuration like this:

app.get(
  '/doc',
  apiReference({
    // Your configuration goes here…
    url: '…'
  }),
)

OpenAPI documentsCopied!

There is just one thing that is really required to render at least something: The content.

There are a bunch of ways to pass your OpenAPI document:

URLCopied!

Pass an absolute or relative URL to your OpenAPI document.

Scalar.createApiReference('#app', {
  url: '/openapi.json'
})

This can be JSON or YAML.

It’s the recommended way to pass your OpenAPI document. In most cases, the OpenAPI document can be cached by the browser and subsequent requests are pretty fast then, even if the document grows over time.

No OpenAPI document? All backend frameworks have some kind of OpenAPI generator. Just search for "yourframework generate openapi document".

ContentCopied!

While this approach is convenient for quick setup, it may impact performance for large documents. For optimal performance with extensive OpenAPI specifications, consider using a URL to an external OpenAPI document instead.

You can just directly pass JSON/YAML content:

Scalar.createApiReference('#app', {
  content: `{
    "openapi": "3.1.0",
    "info": {
      "title": "Hello World",
      "version": "1.0.0"
    },
    "paths": {
      // …
    }
  }`
})

Multiple DocumentsCopied!

Add multiple OpenAPI documents to render all of them. We will need a slug and title to distinguish them in the UI and in the URL. You can just omit those attributes, and we will try our best to still distinguish them, though.

Scalar.createApiReference('#app', {
  sources: [
    // API #1
    {
      title: 'Scalar Galaxy', // optional, would fallback to 'API #1'
      slug: 'scalar-galaxy', // optional, would be auto-generated from the title or the index
      url: 'https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json',
    },
    // API #2
    {
      url: 'https://example.com/openapi.json',
    },
    // API #3
    {
      content: '{ "openapi": "3.1.1", … }',
    }
  ]
})

The first one in the list is the default one. Sometimes, this list is auto-generated and you might want to explicitly set the default like this:

Scalar.createApiReference('#app', {
  sources: [
    // API #1
    {
      url: 'https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json',
    },
    // API #2
    {
      url: 'https://example.com/openapi.json',
      // This will make it the default OpenAPI document:
      default: true,
    }
  ]
})

Multiple ConfigurationsCopied!

Sometimes, you want to modify the configuration for your OpenAPI documents. And the good news is: You can.

Pass an array of configurations to render multiple documents with specific configurations:

Scalar.createApiReference('#app', [
  // Configuration #1
  {
    title: 'Scalar Galaxy', // optional, would fallback to 'API #1'
    slug: 'scalar-galaxy', // optional, would be auto-generated from the title or the index
    url: 'https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json',
    customCss: `body { background-color: #BADA55}`
  },
  // Configuration #2
  {
    url: 'https://example.com/openapi.json',
    customCss: `body { background-color: #c0ffee}`
  },
  // Configuration #3
  {
    content: '{ "openapi": "3.1.1", … }',
    customCss: `body { background-color: #facade}`
  }
])

By default, the first one in the list will be the default configuration. You can explicitly set one with default: true:

Scalar.createApiReference('#app', [
  {
    url: 'https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json',
  },
  {
    url: 'https://example.com/openapi.json',
  },
  {
    // Make this the default configuration:
    default: true,
    content: '{ "openapi": "3.1.1", … }',
  }
])

Multiple Configurations with Sources (Advanced)Copied!

You can even combine multiple configurations that each have their own set of sources. This gives you maximum flexibility in organizing and configuring your API documents:

Scalar.createApiReference('#app', [
  // Configuration #1
  {
    // With sources! 🤯
    sources: [
      // API #1
      {
        url: 'https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json',
      },
      // API #2
      {
        url: 'https://example.com/openapi.json',
        // This will make it the default OpenAPI document:
        default: true,
      }
    ]
  },
  // Configuration #2
  {
    url: 'https://example.com/openapi.json',
  },
  // Configuration #3
  {
    sources: [
      {
        content: '{ "openapi": "3.1.1", … }',
      }
    ]
  }
])

JSON or YAMLCopied!

It is completely up to you whether you want to pass JSON or YAML. None of the differences make really a big difference, but here is a short overview:

  • JSON is faster to parse for the browser
  • JSON is supported natively in basically any environment
  • YAML is easier to write, especially if you want multiline text (for example for descriptions)
  • YAML is easier to read for humans
  • YAML documents tend to be a little bit smaller

List of all attributesCopied!

content?: string | Record<string, any> | () => Record<string, any>Copied!

Directly pass an OpenAPI/Swagger document (JSON or YAML) as a string:

{
  content: '{ "openapi": "3.1.1" }'
}

Or as a JavaScript object:

{
  content: {
    openapi: '3.1.1',
  }
}

Or as a callback returning the actual document:

{
  content() {
    return {
      openapi: '3.1.1',
    }
  }
}

url?: stringCopied!

Pass the URL of an OpenAPI document (JSON or YAML).

{
  url: '/openapi.json'
}

proxyUrl?: stringCopied!

Making requests to other domains is restricted in the browser and requires CORS headers. It's recommended to use a proxy to send requests to other origins.

{
  proxyUrl: 'https://proxy.example.com'
}

You can use our hosted proxy:

{
  proxyUrl: 'https://proxy.scalar.com'
}

If you like to run your own, check out our example proxy written in Go.

plugins?: ApiReferencePlugin[]Copied!

Pass an array of custom plugins that you want. Read more about plugins here.

{
  plugins: [
    XCustomExtensionPlugin(),
  ],
}

showSidebar?: booleanCopied!

Whether the sidebar should be shown.

{
  showSidebar: true
}

hideModels?: booleanCopied!

Whether models (components.schemas or definitions) should be shown in the sidebar, search and content.

@default false

{
  hideModels: true
}

documentDownloadType?: 'json' | 'yaml' | 'both' | 'none'Copied!

Sets the file type of the document to download, set to none to hide the download button

@default 'both'

{
  documentDownloadType: 'json'
}

hideDownloadButton?: booleanCopied!

Whether to show the "Download OpenAPI Document" button

@deprecated Use documentDownloadType: 'none' instead

{
  hideDownloadButton: true
}

hideTestRequestButton?: booleanCopied!

Whether to show the "Test Request" button

@default false

{
  hideTestRequestButton: true
}

hideSearch?: booleanCopied!

Whether to show the sidebar search bar

@default false

{
  hideSearch: true
}

darkMode?: booleanCopied!

Whether dark mode is on or off initially (light mode)

{
  darkMode: true
}

forceDarkModeState?: 'dark' | 'light'Copied!

forceDarkModeState makes it always this state no matter what

{
  forceDarkModeState: 'dark'
}

hideDarkModeToggle?: booleanCopied!

Whether to show the dark mode toggle

{
  hideDarkModeToggle: true
}

layout?: 'modern' | 'classic'Copied!

The layout style to use for the API reference.

{
  layout: 'modern' // or 'classic'
}

isLoading?: booleanCopied!

Controls whether the references show a loading state in the intro section. Useful when you want to indicate that content is being loaded.

@default false

{
  isLoading: true
}

customCss?: stringCopied!

You can pass custom CSS directly to the component. This is helpful for the integrations for Fastify, Express, Hono and others where you it's easier to add CSS to the configuration.

In Vue or React you'd probably use other ways to add custom CSS.

{
  customCss: `* { font-family: "Comic Sans MS", cursive, sans-serif; }`
}

searchHotKey?: stringCopied!

Key used with CTRL/CMD to open the search modal (defaults to 'k' e.g. CMD+k)

{
  searchHotKey: 'l'
}

baseServerURL?: stringCopied!

If you want to prefix all relative servers with a base URL, you can do so here.

{
  baseServerURL: 'https://scalar.com'
}

servers?: Server[]Copied!

Pass a list of servers to override the servers in your OpenAPI document.

{
  servers: [
    {
      url: 'https://api.scalar.com',
      description: 'Production',
    },
    {
      url: 'https://sandbox.scalar.com:{port}',
      description: 'Development sandboxes',
      variables: {
        port: {
          default: '8080',
        }
      }
    },
  ]
}

metaData?: objectCopied!

You can pass information to the config object to configure meta information out of the box.

{
  metaData: {
    title: 'Page title',
    description: 'My page page',
    ogDescription: 'Still about my my page',
    ogTitle: 'Page title',
    ogImage: 'https://example.com/image.png',
    twitterCard: 'summary_large_image',
    // Add more...
  }
}

favicon?: stringCopied!

You can specify the path to a favicon to be used for the documentation.

{
  favicon: '/favicon.svg'
}

defaultHttpClient?: HttpClientStateCopied!

By default, we're using Shell/curl as the default HTTP client. Or, if that's disabled (through hiddenClients), we're just using the first available HTTP client.

You can explicitly set the default HTTP client, though:

{
  defaultHttpClient: {
    targetKey: 'node',
    clientKey: 'undici',
  }
}

hiddenClients?: array | trueCopied!

We're generating code examples for a long list of popular HTTP clients. You can control which are shown by passing an array of clients, to hide the given clients.

Pass an empty array [] to show all available clients.

{
  hiddenClients: []
}

Pass an array of individual clients to hide just those clients.

{
  hiddenClients: ['fetch']
}

Here's a list of all clients that you can potentially hide:

{
  hiddenClients: ['libcurl', 'clj_http', 'httpclient', 'restsharp', 'native', 'http1.1', 'asynchttp', 'nethttp', 'okhttp', 'unirest', 'xhr', 'axios', 'fetch', 'jquery', 'okhttp', 'native', 'request', 'unirest', 'axios', 'fetch', 'nsurlsession', 'cohttp', 'curl', 'guzzle', 'http1', 'http2', 'webrequest', 'restmethod', 'python3', 'requests', 'httr', 'native', 'curl', 'httpie', 'wget', 'nsurlsession', 'undici'],
}

But you can also pass true to hide all HTTP clients. If you have any custom code examples (x-scalar-examples) in your API definition, these still render.

{
  hiddenClients: true
}

onDocumentSelect?: () => voidCopied!

Triggered when multiple documents are configured and the users switches between them.

onSpecUpdate?: (spec: string) => voidCopied!

You can listen to changes with onSpecUpdate that runs on spec/swagger content change

{
  onSpecUpdate: (value: string) => {
    console.log('Content updated:', value)
  }
}

onServerChange?: (server: string) => voidCopied!

You can listen to changes with onServerChange that runs on server change

{
  onServerChange: (value: string) => {
    console.log('Server updated:', value)
  }
}

authentication?: AuthenticationConfigurationCopied!

To make authentication easier you can prefill the credentials for your users:

{
  authentication: {
    // The OpenAPI file has keys for all security schemes.
    // Specify which security scheme(s) should be used by default.
    // Can be a single string:
    preferredSecurityScheme: 'my_custom_security_scheme',
    // Or an array for multiple schemes (OR relationship):
    preferredSecurityScheme: ['scheme1', 'scheme2'],
    // Or an array of arrays for complex AND/OR relationships:
    preferredSecurityScheme: [['scheme1', 'scheme2'], 'scheme3'],

    // Define security scheme configurations:
    securitySchemes: {
      // For API Key
      apiKeyHeader: {
        name: 'X-API-KEY',
        in: 'header',
        value: 'tokenValue',
      },
      // For HTTP Bearer
      httpBearer: {
        token: 'xyz token value',
      },
      // For HTTP Basic
      httpBasic: {
        username: 'username',
        password: 'password',
      },
      // For OAuth2
      oauth2: {
        flows: {
          authorizationCode: {
            // Provide a token that is used instead of calling the auth provider
            token: 'auth code token',
            // Prefill client id or secret
            'x-scalar-client-id': 'your-client-id',
            clientSecret: 'your-client-secret',
            // Overwrite values from the OpenAPI document
            authorizationUrl: 'https://auth.example.com/oauth2/authorize',
            tokenUrl: 'https://auth.example.com/oauth2/token',
            'x-scalar-redirect-uri': 'https://your-app.com/callback',
            // Use PKCE for additional security: 'SHA-256', 'plain', or 'no'
            'x-usePkce': 'SHA-256',
            // Preselected scopes
            selectedScopes: ['profile', 'email'],
            // Set additional query parameters for the Authorization request 
            'x-scalar-security-query': {
              prompt: 'consent',
              audience: 'scalar'
            }
          },
          clientCredentials: {
            token: 'client credentials token',
            'x-scalar-client-id': 'your-client-id',
            clientSecret: 'your-client-secret',
            tokenUrl: 'https://auth.example.com/oauth2/token',
            // Preselected scopes
            selectedScopes: ['profile', 'api:read']
          },
          implicit: {
            token: 'implicit flow token',
            'x-scalar-client-id': 'your-client-id',
            authorizationUrl: 'https://auth.example.com/oauth2/authorize',
            'x-scalar-redirect-uri': 'https://your-app.com/callback',
            // Preselected scopes
            selectedScopes: ['openid', 'profile']
          },
          password: {
            token: 'password flow token',
            'x-scalar-client-id': 'your-client-id',
            clientSecret: 'your-client-secret',
            tokenUrl: 'https://auth.example.com/oauth2/token',
            username: 'default-username',
            password: 'default-password',
            selectedScopes: ['profile', 'email']
          },
        },
        // Set default scopes for all flows
        'x-default-scopes': ['profile', 'email']
      }
    },
  }
}

The authentication configuration accepts:

  • preferredSecurityScheme: Specifies which security scheme(s) to use by default. Can be:
    • A single security scheme name (string)
    • An array of security scheme names (OR relationship)
    • An array containing strings or arrays of strings (AND/OR relationship)
  • securitySchemes: An object mapping security scheme names to their configurations. Each security scheme can be configured with type-specific options.

generateHeadingSlug?: (heading: Heading) => stringCopied!

Customize how heading URLs are generated. This function receives the heading and returns a string ID that controls the entire URL hash.

Note: This must be passed through JavaScript, setting a data attribute will not work.

// Default behavior - results in hash: #description/heading-slug
{
  generateHeadingSlug: (heading) => `#description/${heading.slug}`
}

// Custom example
{
  generateHeadingSlug: (heading) => `#custom-section/${heading.slug}`
}

generateModelSlug?: (model: { name: string }) => stringCopied!

Customize how model URLs are generated. This function receives the model object and returns a string ID. Note that model/ will automatically be prepended to the result.

Note: This must be passed through JavaScript, setting a data attribute will not work.

// Default behavior - results in hash: #model/model-name
{
  generateModelSlug: (model) => slug(model.name)
}

// Custom example - results in hash: #model/custom-prefix-model-name
{
  generateModelSlug: (model) => `custom-prefix-${model.name.toLowerCase()}`
}

generateTagSlug?: (tag: Tag) => stringCopied!

Customize how tag URLs are generated. This function receives the tag object and returns a string ID. Note that tag/ will automatically be prepended to the result.

Note: This must be passed through JavaScript, setting a data attribute will not work.

// Default behavior - results in hash: #tag/tag-name
{
  generateTagSlug: (tag) => slug(tag.name)
}

// Custom example - results in hash: #tag/v1-tag-name
{
  generateTagSlug: (tag) => `v1-${tag.name.toLowerCase()}`
}

generateOperationSlug?: (operation: Operation) => stringCopied!

Customize how operation URLs are generated. This function receives the operation object containing path, operationId, method, and summary. Note that tag/tag-name/ will automatically be prepended to the result.

Note: This must be passed through JavaScript, setting a data attribute will not work.

// Default behavior - results in hash: #tag/tag-name/post-path
{
  generateOperationSlug: (operation) => `${operation.method}${operation.path}`
}

// Custom example - results in hash: #tag/tag-name/v1-post-users
{
  generateOperationSlug: (operation) =>
    `v1-${operation.method.toLowerCase()}-${operation.path.replace('/', '-')}`
}

generateWebhookSlug?: (webhook: { name: string; method?: string }) => stringCopied!

Customize how webhook URLs are generated. This function receives the webhook object containing the name and an optional HTTP method. Note that webhook/ will automatically be prepended to the result.

Note: This must be passed through JavaScript, setting a data attribute will not work.

// Default behavior - results in hash: #webhook/webhook-name
{
  generateWebhookSlug: (webhook) => slug(webhook.name)
}

// Custom example - results in hash: #webhook/v1-post-user-created
{
  generateWebhookSlug: (webhook) =>
    `v1-${webhook.method?.toLowerCase()}-${webhook.name}`
}

pathRouting?: { basePath: string }Copied!

Configuration for path-based routing instead of hash-based routing. Your server must support this routing method.

{
  pathRouting: {
    basePath: '/standalone-api-reference/:custom(.*)?'
  }
}

redirect?: (path: string) => string | null | undefinedCopied!

Function to handle redirects in the API reference. Receives either:

  • The current path with hash if pathRouting is enabled
  • The current hash if using hashRouting (default)
// Example for hashRouting (default)
{
  redirect: (hash) => hash.replace('#v1/old-path', '#v2/new-path')
}

// Example for pathRouting
{
  redirect: (pathWithHash) => {
    if (pathWithHash.includes('#')) {
      return pathWithHash.replace('/v1/tags/user#operation/get-user', '/v1/tags/user/operation/get-user')
    }
    return null
  }
}

onLoaded?: () => voidCopied!

Callback that triggers as soon as the references are lazy loaded.

Note: This must be passed through JavaScript, setting a data attribute will not work.

{
  onLoaded: () => {
    console.log('References loaded')
  }
}

onShowMore?: (tagId: string) => void | Promise Copied!

Callback function that is triggered when a user clicks the "Show more" button in the references. The function receives the ID of the tag that was clicked.

{
  onShowMore: (tagId) => {
    console.log('Show more clicked for tag:', tagId)
  }
}

onSidebarClick?: (href: string) => void | Promise Copied!

Callback function that is triggered when a user clicks on any item in the sidebar. The function receives the href of the clicked item.

{
  onSidebarClick: (href) => {
    console.log('Sidebar item clicked:', href)
  }
}

onRequestSent?: (request: string) => voidCopied!

Callback function that is triggered when a request is sent through the API client. The function receives the request details as a string.

{
  onRequestSent: (request) => {
    console.log('Request sent:', request)
  }
}

persistAuth?: booleanCopied!

Whether to persist authentication credentials in local storage. This allows the authentication state to be maintained across page reloads.

@default false

{
  persistAuth: true
}

[!WARNING] Persisting authentication information in the browser's local storage may present security risks in certain environments. Use this feature with caution based on your security requirements.

withDefaultFonts?: booleanCopied!

By default we're using Inter and JetBrains Mono, served from our fonts CDN at https://fonts.scalar.com. If you use a different font or just don't want to load web fonts, pass withDefaultFonts: false to the configuration.

{
  withDefaultFonts: false
}

defaultOpenAllTags?: booleanCopied!

By default we only open the relevant tag based on the url, however if you want all the tags open by default then set this configuration option :)

{
  defaultOpenAllTags: true
}

tagsSorter?: 'alpha' | (a: Tag, b: Tag) => numberCopied!

Sort tags alphanumerically ('alpha'):

{
  tagsSorter: 'alpha'
}

Or specify a custom function to sort the tags.

Note: Most of our integrations pass the configuration as JSON and you can't use custom sort functions there. It will work in Vue, Nuxt, React, Next and all integrations that don't need to pass the configuration as a JSON string.

{
  /** @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort */
  tagsSorter: (a, b) => {
    if (a.name === 'Super Important Tag') return -1
    return 1
  }
}

Learn more about Array sort functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

operationsSorter?: 'alpha' | 'method' | ((a: TransformedOperation, b: TransformedOperation) => number)Copied!

{
  operationsSorter: 'alpha'
}

Or specify a custom function to sort the operations.

{
  operationsSorter: (a, b) => {
    const methodOrder = ['GET', 'POST', 'PUT', 'DELETE']
    const methodComparison = methodOrder.indexOf(a.httpVerb) - methodOrder.indexOf(b.httpVerb)

    if (methodComparison !== 0) {
      return methodComparison
    }

    return a.path.localeCompare(b.path)
  },
}

theme?: stringCopied!

You don't like the color scheme? We've prepared some themes for you:

Can be one of: alternate, default, moon, purple, solarized, bluePlanet, saturn, kepler, mars, deepSpace, laserwave, none

{
  theme: 'default'
}

hideClientButton?: booleanCopied!

Whether to show the client button from the reference sidebar and modal

@default false

{
  hideClientButton: true
}