Nested f-strings in Python
On Python 3.11 and earlier, an f-string raises SyntaxError when an expression inside its braces reuses the outer quote character, so write f"{data['name']}" instead of f"{data["name"]}". Python 3.12 and newer (PEP 701) accept the same quotes and backslashes inside the braces, but a backslash-escaped quote such as \" in an expression is still a SyntaxError on every version. The other kind of nesting, a field inside the format spec like f"{value:.{n}f}", makes precision, width, or fill dynamic.
Quick answer: To fix a nested-quote SyntaxError on Python 3.11 or earlier, switch quote styles inside the braces (f"{data['name']}") and move any backslash out of the expression into a variable. On Python 3.12+, reusing the same quotes works: f"{data["name"]}". On every version, never escape quotes with a backslash inside the braces. To make precision dynamic, nest a field in the spec: f"{value:.{n}f}".
What Is a Nested f-string in Python?
A nested f-string is an f-string with a second replacement field or a second f-string inside its curly braces. The common form computes the format spec at runtime, as in f"{value:.{n}f}"; the rarer form uses a literal f-string as the expression, as in f"{f'inner {x}'}", and that form, like any quoted string inside the braces such as a dict key, must not reuse the outer quote character on Python 3.11 and earlier.
How Do You Set Decimal Places with a Variable in an f-string?
Put the variable in its own braces inside the format spec: f"{value:.{n}f}" formats value with n decimal places. Python evaluates the inner field first, so with n = 2 the spec becomes .2f before it is applied.
value = 3.14159265359
for n in range(1, 5):
print(f"{value:.{n}f}")
# 3.1
# 3.14
# 3.142
# 3.1416
How Do You Set f-string Width or Padding with a Variable?
Nest the width variable inside the alignment spec: f"{name:<{width}}" left-aligns name in a field width characters wide, padded with spaces. Compute the width once, for example from the longest value, to line up a column of text.
names = ["Alice", "Bob", "Charlie"]
width = max(len(n) for n in names) # 7
for n in names:
print(f"{n:<{width}} | done")
# Alice | done
# Bob | done
# Charlie | done
Dynamic Fill Character
The fill character can come from a variable too. It goes right before the alignment character (<, ^, or >).
fill = "*"
title = "REPORT"
f"{title:{fill}^20}"
# '*******REPORT*******'
Conditional Formatting Inside an f-string
Use a ternary expression โ this isn't truly "nested," but it's the second-most-common reason people search for nested f-strings.
change = -0.042
f"{('โผ' if change < 0 else 'โฒ')} {abs(change):.1%}"
# 'โผ 4.2%'
# Conditional precision:
n = 0
f"{value:.{2 if n == 0 else n}f}" # '3.14' when n is 0
The Quote Rule (Python 3.11 and Earlier)
Before Python 3.12 you could not reuse the same quote character inside an f-string expression. The tokenizer read the whole f-string as one string literal, so a matching quote inside the braces ended the string early. This forces you to alternate quote styles:
# Python 3.11 โ must use the OTHER quote inside braces:
data = {"name": "Alice"}
f"{data['name']}" # OK โ single quotes inside double-quoted f-string
# f"{data["name"]}" # 3.11: SyntaxError: f-string: unmatched '['
# To nest f-strings, alternate:
inner = "world"
f"hello {f'{inner}!'}" # 'hello world!'
# f"hello {f"{inner}!"}" # 3.11: SyntaxError: f-string: expecting '}'
Python 3.12+ Relaxed the Quote Rule (PEP 701)
PEP 701 formalized f-strings in Python's grammar in 3.12, which lifted the quote and backslash restrictions on the expressions inside the braces.
# Python 3.12+ โ same quotes are fine:
data = {"name": "Alice"}
f"{data["name"]}" # 'Alice' (would error pre-3.12)
# You can also nest f-strings with the same quotes:
f"hello {f"{inner}!"}" # 'hello world!' on 3.12+
# And backslashes are allowed in expressions:
items = ["a", "b"]
f"{'\n'.join(items)}" # OK on 3.12+, SyntaxError before
When to Use a Nested f-string vs format()
If the format spec is fully dynamic and reused, str.format() is often clearer:
spec = ".2f"
value = 3.14159
# Nested f-string:
f"{value:{spec}}" # '3.14'
# format() โ same result, sometimes cleaner:
"{:{}}".format(value, spec) # '3.14'
# format_map for many fields:
"{value:{spec}}".format_map({"value": value, "spec": spec})
Common Pitfalls
Most nested f-string errors come from quotes, backslashes, or nesting too deeply.
- Quotes in expressions on Python โค 3.11: alternate quote styles, or assign to a variable first.
- No backslashes in expressions on โค 3.11:
f"{'\n'}"is a SyntaxError. Usechr(10)or assign the value first. - Don't backslash-escape quotes inside the braces:
f"{data[\"name\"]}"orf"{\", \".join(items)}"raisesSyntaxError: unexpected character after line continuation characteron 3.12+ (and "f-string expression part cannot include a backslash" on 3.11). Use the other quote style (f"{', '.join(items)}"), or on 3.12+ the same quote unescaped:f"{data["name"]}". - Nesting depth (all versions): on 3.11 and earlier, a format spec can contain a replacement field, and that field can have its own spec (
:.{n:d}f), but it cannot contain another nested field:f"{v:{w:{x}}}"raises "f-string: expressions nested too deeply". Python 3.12+ allows one more level, sof"{v:{w:{x}}}"compiles, but a fourth level such asf"{v:{w:{x:{y}}}}"still raises the same error. - Readability: even where a three-level nest compiles (3.12+), it is hard to read. Pull complex logic into a variable.
Pro Tip: Check your Python version with python --version. If you're on 3.12 or newer, the PEP 701 relaxation means most "nested f-string is a SyntaxError" advice on Stack Overflow is outdated. Backslash-escaped quotes inside the braces still fail, though; see Common Pitfalls above.