Tutorials Search / Native Mac IDE / Create a custom SF Symbol
📝 Written ● Intermediate Updated 2026-05-17

Create a custom SF Symbol

SF Symbols ships with 6,000+ glyphs that scale, weight, and color exactly like text. Sometimes the one you need isn't in the set — usually your own logo or a domain-specific glyph. The fix is to make your own that behaves the same way: scales with Dynamic Type, picks up .foregroundColor, swaps weights with the system font. Here's the whole pipeline.

The thing that makes SF Symbols special isn't that they're vectors — it's that they're indexed against the system font. When the user picks an XL Dynamic Type size, every system symbol scales with them. When you switch to a bold font weight, the symbol's strokes thicken. Your custom one can do this too, but only if you ship it in the right template format and import it into the asset catalog as a "Symbol Image" — not just any old SVG.

What you'll learn

Step 1: The mental model

1

It's an SVG, but with required IDs and guides

A "custom symbol" SVG is a regular SVG file with specific layer IDs, three horizontal guide lines, and (usually) nine pre-drawn variants — three sizes (Small, Regular, Large) × three weights (Ultralight, Regular, Black). The SF Symbols app expects exactly this structure, and Xcode's "Symbol Image" asset type knows how to read it.

You don't draw all nine variants from scratch. The standard workflow is:

  1. Pick a system symbol close to what you want.
  2. Export it as a template from the SF Symbols app — that gives you the SVG scaffold with all the IDs and guides in place.
  3. Replace the artwork in each variant (or just one if you don't need the full set).
  4. Save and drop into the asset catalog.

Once it's there, your custom symbol behaves like a system one — Image("custom-name") in SwiftUI, .font(.title) to scale it, .foregroundStyle(.red) to color it.

Step 2: Get the template from SF Symbols.app

2

Export a similar symbol as a starting point

Open the SF Symbols app (free from Apple). Find a system symbol with a shape and proportion similar to what you want to draw. Right-click it and choose Export Symbol… — the dialog asks where to save and which template version to use (pick the latest your minimum-OS target supports).

The exported file opens in any vector tool — Figma, Illustrator, Sketch, even Inkscape. What you'll see:

  • Three rows (top to bottom): Small, Regular, Large size variants
  • Three columns (left to right): Ultralight, Regular, Black weight variants
  • Three horizontal guides on each glyph: Cap Height, Baseline, Descender — borrowed from font typography

The guides matter. They're what align your symbol with the surrounding text when you put it inline. If your glyph's vertical extents don't match the guides, the symbol will look misaligned next to a word.

You don't have to draw all nine variants. The minimum is one — the "Regular Regular" cell in the center. Xcode will derive Small/Large by scaling, and Ultralight/Black by reusing your Regular. Quality is best with all nine drawn, but for an internal tool or a v0 ship, one is enough.
Need a starting glyph? Use chat image generation. LingCode's chat has a built-in generate_image tool wired to Gemini. Ask "generate a 1024×1024 monochrome glyph: a stylized compass rose, black on transparent, suitable for an icon" and the agent saves the result to <project>/assets/. The output won't be template-formatted yet — but it gives you a clean reference shape to trace in Figma or import directly into Step 4's agent prompt as the source artwork. Treat it as a sketch tool, not the final asset.

Step 3: Replace the artwork

3

Swap glyphs without disturbing IDs or guides

In your vector tool, select the center "Regular Regular" glyph and replace it with your artwork. The constraints:

  • Don't move or rename layers outside the glyph cell. The IDs Symbols, Notes, Guides, and the variant-cell names are what the SF Symbols importer reads. Renaming them breaks import.
  • Stay between the Cap Height and Baseline guides for the visual body of the glyph. Anything below the Baseline (like a descender) needs to land above the Descender guide.
  • Use strokes or filled paths, not both freely. Symbols rendered in .symbolRenderingMode(.hierarchical) color paths by layer order; if your shapes are inconsistent the colors land in the wrong place.

For the other variants: copy the Regular cell into Small and Large (Xcode will scale these from the center cell anyway, but providing pre-scaled versions gives crisper results). For weights, duplicate Regular into Ultralight (thinner strokes) and Black (heavier strokes) if you have the time. If not, leave them and Xcode falls back to the Regular weight.

Step 4: Let the agent generate the template

4

Faster than hand-editing if you have a logo SVG

If your logo is already a clean SVG, the agent can usually wrap it in a valid symbol template without you opening Figma. Open a Claude chat in your project and ask:

I have a logo at ~/Desktop/brand-mark.svg. Generate a custom SF Symbol
template (Symbol 4.0, 9-variant) that uses this logo as the Regular Regular
cell and scales it for the other 8 cells. Save it as
MyApp/Assets.xcassets/BrandMark.symbolset/BrandMark.svg with the matching
Contents.json.

The agent will produce two files: the template SVG (with all nine cells, correct IDs, correct guides) and a small Contents.json that registers it as a Symbol Image. Verify by opening the asset catalog in Xcode — the new symbol should appear under the Symbol Image category, with a preview at each weight.

If the agent's first attempt looks off — usually because your source logo's proportions don't match font-glyph proportions — paste a screenshot of the asset-catalog preview back and ask it to adjust the scale or vertical position.

Step 5: Use it in SwiftUI

5

One argument changes — systemName becomes the bare name

System symbols use systemName. Custom symbols don't:

// System symbol
Image(systemName: "star.fill")
    .font(.title)
    .foregroundStyle(.yellow)

// Custom symbol — same modifiers, different initializer
Image("BrandMark")
    .font(.title)
    .foregroundStyle(.yellow)

That's the entire API difference. Once your custom symbol is in the asset catalog, every SF Symbols modifier — .font(), .foregroundStyle(), .symbolRenderingMode(), .symbolEffect() — works on it identically. You can put your logo and "star.fill" in the same HStack and they'll align, scale, and color together.

UIKit equivalent is UIImage(named: "BrandMark", in: nil, with: nil) for custom vs. UIImage(systemName:) for system.

If your custom symbol shows as a solid rectangle, the import failed silently. Almost always one of: a non-SVG file (a PNG with a .svg extension counts), missing layer IDs in the template, or the asset catalog entry is a regular Image Set rather than a Symbol Image Set. Delete the entry, drag the SVG in again — Xcode's "New Symbol Image Set" creates the correct entry type. If you got there via "New Image Set" you're in the wrong slot.

Step 6: When to use a custom symbol vs. an image

6

Heuristic: would it sit next to text?

Custom symbols are worth the overhead when:

  • The glyph sits inline with text — buttons, list rows, navigation items, badges. Dynamic Type scaling matters here.
  • It needs to match the system font's weight — your bold headline shouldn't have a regular-weight icon next to it.
  • You want it to pick up .foregroundStyle automatically rather than ship multiple color variants.

Skip the custom-symbol pipeline and use a regular vector or PNG image when:

  • The image is large and standalone — onboarding hero, empty-state illustration, splash content. Symbols are designed for inline use; they look thin and lifeless at hero scale.
  • It needs multiple intrinsic colors that don't follow the symbol-rendering modes (hierarchical, palette, multicolor). For a brand mark that's always a specific gradient, just ship a PDF asset.
  • You don't need Dynamic Type scaling. A static-size product photo doesn't benefit from symbol semantics.

What's next