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.
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:
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.
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:
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.
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.
In your vector tool, select the center "Regular Regular" glyph and replace it with your artwork. The constraints:
Symbols, Notes, Guides, and the variant-cell names are what the SF Symbols importer reads. Renaming them breaks import..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.
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.
systemName becomes the bare nameSystem 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.
.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.
Custom symbols are worth the overhead when:
.foregroundStyle automatically rather than ship multiple color variants.Skip the custom-symbol pipeline and use a regular vector or PNG image when: