Skip to content
My profile picture Kushajveer Singh




Install starlight

npm create astro@latest -- --template starlight


  • ./ - Where should we create your new project?
  • Yes - Install dependencies?
  • Yes - Do you plan to write TypeScript?
  • Strictest - How strict should TypeScript be?

Make the following changes

  • Delete everything in public and place your desired favicon file here. Then add the path to favicon file in astro.config.mjs.
    export default defineConfig({
      integrations: [
          favicon: '/favicon.ico',
  • Delete everything in src/assets folder. This folder is used to store all the images.
  • Delete everything in src/content/docs except src/content/docs/index.mdx and change index.mdx to the following
    title: Hello world
    template: splash
    Hello world
  • Remove sidebar property in astro.config.mjs
  • Modify src/content/config.ts to remove i18n support (otherwise it shows a warning when you run npm run dev)
    import { defineCollection } from 'astro:content';
    import { docsSchema } from '@astrojs/starlight/schema';
    export const collections = {
      docs: defineCollection({ schema: docsSchema() }),
  • Modify the name property in package.json.

The folder structure should be as follows

├── node_modules
├── public
   └── favicon.ico
├── src
   ├── assets
   ├── content
      ├── config.ts
      └── docs
          └── index.mdx
   └── env.d.ts
├── .gitignore
├── astro.config.mjs
├── package-lock.json
├── package.json
└── tsconfig.json

Add starlight-links-validator support.

npm install starlight-links-validator

Update astro.config.mjs to include Starlight Links Validator integration before the Starlight integration

import starlightLinksValidator from 'starlight-links-validator';

export default defineConfig({
  integrations: [

Modify Header

To show headings like “Work”, “Project”, “About” in the header use the following instructions.

  1. Copy the content of node_modules/@astrojs/starlight/components/Header.astro and paste in src/components/Header.astro.
  2. To remove TypeScript error warning add // @ts-ignore in src/components/Header.astro for the imports as shown below
    // @ts-ignore
    import LanguageSelect from './LanguageSelect.astro';
    // @ts-ignore
    import Search from './Search.astro';
    // @ts-ignore
    import SiteTitle from './SiteTitle.astro';
    // @ts-ignore
    import SocialIcons from './SocialIcons.astro';
    // @ts-ignore
    import ThemeSelect from './ThemeSelect.astro';
  3. Modify the Header components to include your headings
    <div class="header flex">
      <SiteTitle {locale} />
      <Search {locale} />
      <div class="hidden md:flex right-group">
        <a class="link_tag" href="/work">Work</a>
        <a class="link_tag" href="/project">Project</a>
        <a class="link_tag" href="/blog">Blog</a>
        <a class="link_tag" href="/about">About</a>
        <SocialIcons />
        <ThemeSelect {locale} />
        <LanguageSelect {locale} />
      .link_tag {
        color: var(--sl-color-text-accent);
        text-decoration: none;
        white-space: nowrap;
  4. The changes you made in the previous step, should also be copied to node_modules/@astrojs/starlight/components/Header.astro. This header file would be used for the local builds i.e. when you run npm run dev. But when deploying to GitHub, we would copy src/components/Header.astro to note_modules folder.

This needs to be changed every time you upgrade Starlight.


Install Astro extension.


npm install --save-dev @typescript-eslint/parser

Create .eslintrc.js in the root folder and copy the following text

module.exports = {
  // ...
  extends: [
    // ...
  // ...
  overrides: [
      // Define the configuration for `.astro` file.
      files: ["*.astro"],
      // Allows Astro components to be parsed.
      parser: "astro-eslint-parser",
      // Parse the script in `.astro` as TypeScript by adding the following configuration.
      // It's the setting you need when using TypeScript.
      parserOptions: {
        parser: "@typescript-eslint/parser",
        extraFileExtensions: [".astro"],
      rules: {
        // override/add rules settings here, such as:
        // "astro/no-set-html-directive": "error"
    // ...

Install ESLint extension for VSCode and modify the .vscode/settings.json file

  "eslint.validate": [
    "astro", // Enable .astro
    "typescript", // Enable .ts
    "typescriptreact" // Enable .tsx


Upgrade to latest version and see CHANGELOG before that

npm install @astrojs/starlight@latest


Add the site property in astro.config.mjs

export default defineConfig({
  site: '',
  base: '/repo-name', // If not using repo


  • .github/actions/withastro_action_v0/action.yml - The code for this is copied from the official withastro/actionv0. After installing all the npm dependencies, we copy src/components/Header.astro to node_modules, which is shown in the step name: Replace astrojs/starlight component.
  • .github/workflows/deploy.yml - This is the official Astro deploy workflow file. The only change is instead of use: withastro/actionv0, we use the local action file uses: ./.github/actions/withastro_action_v0.

Create .github/actions/withastro_action_v0/action.yml and paste the following code

name: "Astro Deploy"
description: "A composite action that prepares your Astro site to be deployed to GitHub Pages"
  icon: "box"
  color: "orange"
    description: "Path of the directory containing your site"
    required: false
    default: "."
    description: "The node version to use"
    required: false
    default: "16"
    description: "If not automatically detectable, you may specify your preferred package manager"
    required: false
    default: ""
  # resolve-dep-from-path:
  #   description: "If the dependency file is located inside the folder specified by path" 
  #   type: boolean
  #   required: false
  #   default: true 

  using: composite
    - name: Check lockfiles
      shell: "bash"
        INPUT_PM: ${{ inputs.package-manager }}
      run: |
        len=`echo $INPUT_PM | wc -c`
        if [ $len -gt 1 ]; then
        elif [ $(find "." -name "pnpm-lock.yaml") ]; then
            echo "PACKAGE_MANAGER=pnpm" >> $GITHUB_ENV
            echo "LOCKFILE=pnpm-lock.yaml" >> $GITHUB_ENV
        elif [ $(find "." -name "yarn.lock") ]; then 
            echo "PACKAGE_MANAGER=yarn" >> $GITHUB_ENV
            echo "LOCKFILE=yarn.lock" >> $GITHUB_ENV
        elif [ $(find "." -name "package-lock.json") ]; then 
            echo "PACKAGE_MANAGER=npm" >> $GITHUB_ENV
            echo "LOCKFILE=package-lock.json" >> $GITHUB_ENV
            echo "No lockfile found.
        Please specify your preferred \"package-manager\" in the action configuration."
            exit 1
    - name: Setup PNPM
      if: ${{ env.PACKAGE_MANAGER == 'pnpm' }}
      uses: pnpm/action-setup@v2.2.4
        version: 7.x.x

    - name: Setup Node
      uses: actions/setup-node@v3
      if: inputs.resolve-dep-from-path == true 
        node-version: ${{ inputs.node-version }}
        cache: ${{ env.PACKAGE_MANAGER }}
        cache-dependency-path: "${{ inputs.path }}/${{ env.LOCKFILE }}"

    - name: Setup Node
      uses: actions/setup-node@v3
      if: inputs.resolve-dep-from-path == false 
        node-version: ${{ inputs.node-version }}
        cache: ${{ env.PACKAGE_MANAGER }}

    - name: Install
      shell: "bash"
      run: |
        cd ${{ inputs.path }}
        $PACKAGE_MANAGER install
    - name: Replace astrojs/starlight component
      shell: "bash"
      run: |
        cp src/components/Header.astro node_modules/@astrojs/starlight/components/Header.astro

    - name: Build
      shell: "bash"
      run: |
        cd ${{ inputs.path }}
        $PACKAGE_MANAGER run build

    - name: Upload Pages Artifact
      uses: actions/upload-pages-artifact@v1
        path: "${{ inputs.path }}/dist/"

Create .github/workflows/deploy.yml

name: Deploy to GitHub Pages

  # Trigger the workflow every time you push to the `main` branch
  # Using a different branch name? Replace `main` with your branch’s name
    branches: [ main ]
  # Allows you to run this workflow manually from the Actions tab on GitHub.

# Allow this job to clone the repo and create a page deployment
  contents: read
  pages: write
  id-token: write

    runs-on: ubuntu-latest
      - name: Checkout your repository using git
        uses: actions/checkout@v3
      - name: Install, build, and upload your site
        uses: ./.github/actions/withastro_action_v0
            # path: . # The root location of your Astro project inside the repository. (optional)
            # node-version: 16 # The specific version of Node that should be used to build your site. Defaults to 16. (optional)
            package-manager: npm # The Node package manager that should be used to install dependencies and build your site. Automatically detected based on your lockfile. (optional)

    needs: build
    runs-on: ubuntu-latest
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1

In your GitHub repo

  • Settings -> Pages
  • Choose GitHub Actions as the Source of your site.

Commit your changes and site should be deployed.

Markdown docs


  • *Italic* - Italic
  • **Bold** - Bold
  • # Heading 1
  • ## Heading 2
  • [link_text](http://link.url)
  • ![image_alt_text](image_link)
  • > Blockquote
  • * Unordered List
  • 1. Ordered List
  • --- - create a horizontal line
  • create code block with 3 backticks or indent by 4 spaces

GitHub Flavored Markdown

  • ~Strikethrough~ - Strikethrough
  • Table
    | foo | bar |
    | --- | --- |
    | baz | bim |
    | --- | - left align
    | :-- | - left align
    | --: | - right align
    | :-: | - center align
  • Task list item
    • foo
    • bar
    - [ ] foo
    - [x] bar
  • Autolink. If you write a link, then it would be automatically rendered as <p><a href=""></a></a>. Note By default, it adds http.

Starlight additions

  • The title in frontmatter is rendered as <h1> and automatically added at the start of the page. So start the headings with <h2> tags in your markdown files.

  • Link to another page [link to another page](/astro_setup) (

  • Link directly to anchor links like [starlight additions](#starlight-additions) (starligh additions).

  • Asides for note, tip, caution, danger. The general syntax is

    :::note[Custom title]
    Your content `code`.


Syntax for importing and using components in MDX files

title: Something

import MyComponent from '/src/components/MyComponent.astro';

<MyComponent prop="something" />

// If you want to disable the default styling of Starlight affecting
// your component
<MyComponent class="not-content" />

Card / CardGrid

To show content in a box use Card. Wrap multiple Card in CardGrid (can be used to show features of your project on the homepage).

import { Card, CardGrid } from '@astrojs/starlight/components';

// Optionally provide "icon" which has to be from this list
<Card title="Check this out" icon="up-caret">Some content.</Card>

Check this out

Some content.
// Optionally pass "stagger" to shift the second column of cards
// vertically and add visual interest.
<CardGrid stagger>
    <Card title="Stars" icon="star">Sirius</Card>
    <Card title="Moons" icon="moon">Io</Card>






You can import icons that are listed at using <Icon>.

import { Icon } from '@astrojs/starlight/components';

// optionally provide label/size/color
<Icon name="star" size="2rem" color="goldenrod" />

Icons are internally represented as SVGs. You can check the source code of the Icon component here. And then copy this component to your repo, and change name to include the SVG text, something like <path d="M22 7.24a1 1 0 0 0-.29-.71l-4.24-4.24a1 1 0 0 0-1.1-.22 1 1 0 0 0-.32.22l-2.83 2.83L2.29 16.05a1 1 0 0 0-.29.71V21a1 1 0 0 0 1 1h4.24a1 1 0 0 0 .76-.29l10.87-10.93L21.71 8c.1-.1.17-.2.22-.33a1 1 0 0 0 0-.24v-.14l.07-.05ZM6.83 20H4v-2.83l9.93-9.93 2.83 2.83L6.83 20ZM18.17 8.66l-2.83-2.83 1.42-1.41 2.82 2.82-1.41 1.42Z"/>.

Starlight issue which tracks support for external icons. One suggestion is to use astro-icon.


import { Tabs, TabItem } from '@astrojs/starlight/components';

    <TabItem label="First">This should be shown first</TabItem>
    <TabItem label="Second">This should be shown second</TabItem>
This should be shown first


View the astro.config.mjs file used for this project at this link.

title: 'Title for website used in metadata and tab title.', 
description: `Description for website used in metadata. Also, 
              the default, if description not passed in frontmatter.`,
logo: {
  src: '/src/assets/profile.jpg', // Comment 'src', if providing light/dark logos
  // light: '/src/assets/profile.jpg',
  // dark: '/src/assets/profile.jpg',
  alt: 'My profile picture',
  replacesTitle: false, // If logo contains your title, then you can hide title

// this can be set globally or per-page in frontmatter
// tableOfContents: false // to disable it
tableOfContents: {
  minHeadingLevel: 2,
  maxHeadingLevel: 3,

// Show "Edit page" links at the bottom of each page. The final link is
// "editLink.baseUrl + CURRENT_PAGE_PATH".
editLink: {
  baseUrl: '{USERNAME}/{REPO}/edit/{BRANCH}/'

sidebar: {
  // Add label and link to the page (can be external)
  { label: 'Home', link: '/' },

  // Add label (group name) and array of item to specify sublinks/subgroups
    label: 'Main group',
    items: [
      { label: 'Sublink 1', link: '/intro' },
      { label: 'Sublink 2', link: '' },

  // Autogenerate all links in a directory (shown in alphabetical order).
  // The order of 'items' in this case can also be controlled by setting
  // 'order' frontmatter on each page.
    label: 'Reference',
    autogenerate: { directory: 'reference' } // 'directory' is name of a
                                             // folder in 'docs'

  // By default, the "label" are expanded in sidebar. You can control that
  // by passing "collapsed: true"
    label: 'Collapsed Links',
    collapsed: true,
    items: [],

// Refer to if you want to add
// support for i18n,
// The below config options will set your site to just use one language.
defaultLocale: 'root',
locales: {
  root: {
    label: 'English',
    lang: 'en',

// Add social media accounts at top-right in the header. See all available
// options at
social: {
  github: '{USERNAME}',

// Add custom css files. Can import local files and files installed using npm
customCss: ['./src/custom-styles.css', '@fontsource/roboto'],

head: [
    tag: 'script',
    attr: {
      src: '',
      'data-size': 'MY-FANTHOM-ID',
      defer: true,
  { // Fallback favicon
    tag: 'link',
    attr: {
      rel: 'icon',
      href: '/images/favicon.ico',
      sizes: '32x32',
// If footer should show when page was last updated
lastUpdated: false,

// If footer should include previous and next page links
pagination: true,

// Should be located in 'public' folder (ico/gif/jpg/png/svg)
favicon: '/favicon.ico',



# Override global editLink config by providing a link or set to false to disable
editUrl: string | boolean

# Add additional tags in page's <head>
    - tag: title
      content: Custom title

# Overrides global tableOfContents
tableOfContents: false | { 
                            minHeadingLevel?: number,
                            maxHeadingLevel?: number,

# 'doc' (default)
# 'splash' - wider layout without sidebars
template: 'doc' | 'splash'

# Use the provided 'Hero' component at the top of pages
# (works well with "template: 'splash'")
    title: 'Make your docs shine with Starlight'
    tagline: Everything you need to build a stellar doc website.
        alt: A really big logo
        file: /assets/logo.png
        # Can provide raw HTML <img> tag using
        html: string
        - text: Get Started
          link: /getting-started/
          icon: right-arrow
          variant: 'primary' # Can be primary/secondary/minimal
        - text: View on GitHub
          icon: external

# Override global. Set to false to disable
lastUpdated: Date | boolean

# Show prev/next links
prev: boolean |
      string |
      { link?: string, label?: string }
# Hide the previous page link
prev: false
# Override the previous page link text
prev: Continue the tutorial
# Override both the previous page link and text
    link: /unrelated-page/
    label: Check out this other page

# How page is displayed in the sidebar when 'autogenerated' is used
sidebar: { label?: string; order?: number }

Extra files


Create src/content/docs/404.mdx. The default 404 pages the following frontmatter

title: '404'
template: splash
editUrl: false
  title: '404'
  tagline: Page not found. Check the URL or try using the search bar.


Configure public/robots.txt as per your requirements

User-agent: *


Put your custom domain here