You add an input to a script so you can change a number without editing code, and an hour later the settings dialog is a flat wall of fifteen unlabeled fields, one dropdown rejects its own default value, and a color picker sits three rows away from the line it controls. Inputs are the part of a Pine Script indicator that other people actually touch, and they are also where a surprising number of compile errors come from.
This guide walks through every input.* function in Pine Script v6: what each one returns, which arguments matter, and the mistakes that cost the most time. By the end you should be able to choose the right input type for a setting, group and label it cleanly, and avoid the handful of errors that trip up almost everyone. If you are coming from an older script, the v6 enum feature deserves its own read, and we cover its input form below.
What an input actually is
An input is a value the user chooses in the script's Settings dialog, on the Inputs tab, before the script runs. Pine Script reads the value once when the script is added or when a setting changes, and then recalculates every bar with that value. Nothing about an input changes while the bars are being processed, which is why input values can be used in places that demand a stable number, such as the length of a moving average or the price of an hline.
Each input.* function takes a default value as its first argument, then a title, and then a set of optional arguments that shape how the field behaves. The same optional arguments appear on most types, so it is worth learning them once. title is the label. tooltip adds a small question mark with a longer explanation. group collects several inputs under one heading. inline places several inputs on the same row. confirm asks the user to confirm the value when the script is first added to a chart. The numeric types add minval, maxval, and step, and the text and number types can take options to turn the field into a dropdown.
Pine Script also has a generic input() function that guesses the type from the default value. It still works, but the explicit functions are clearer to read and give you the type-specific arguments, so the rest of this post sticks to them.
Numbers and switches: input.int, input.float, input.bool, input.source
These four cover most indicators. input.int returns a whole number and is what you use for lengths and bar counts. input.float returns a decimal and fits multipliers, thresholds, and percentages. input.bool is a checkbox. input.source lets the user pick which series feeds a calculation: it lists close, open, high, low, and the others, along with plots from other indicators on the same chart.
The minval and maxval arguments are worth using every time. A length of zero or a negative length is one of the most common reasons an indicator fails at runtime, and setting minval = 1 stops the user from typing it into the dialog at all. Here is a small Bollinger-style indicator that uses all four types.
//@version=6
indicator("Input basics", overlay = true)
src = input.source(close, "Source")
len = input.int(20, "Length", minval = 1, maxval = 500, step = 1, tooltip = "Number of bars in the average")
mult = input.float(2.0, "Multiplier", minval = 0.1, step = 0.1)
showBands = input.bool(true, "Show bands")
basis = ta.sma(src, len)
dev = ta.stdev(src, len) * mult
plot(basis, "Basis", color = color.blue)
plot(showBands ? basis + dev : na, "Upper", color = color.gray)
plot(showBands ? basis - dev : na, "Lower", color = color.gray)Notice that the checkbox hides the bands by returning na instead of wrapping the plot call in an if. A plot call has to run on every bar, so you cannot place it inside a conditional block. This is the same reason the plot in local scope error exists, and a bool input is the cleanest way around it.
One more habit worth building: make input.source the first input of any indicator that is built on price. Users who want to smooth open, hlc3, or another indicator's output can do so without touching your code, and it costs one line.
Text, dropdowns, and colors: input.string and input.color
input.string returns text. On its own it is a free text box, which is rarely what you want. Add the options argument and it becomes a dropdown, which is the right tool for choosing between a short list of named modes, such as the type of moving average.
The single most common mistake here is a default value that is not in the list. input.string("Exponential", "Type", options = ["SMA", "EMA"]) fails to compile because the default has to be one of the options exactly as spelled, including capitalization. Keep the options in a variable-free literal list and copy the default from it.
input.color returns a color and shows a color picker. Offer separate colors for up and down states instead of hard coding them: traders who use dark and light chart themes will thank you. The group and inline arguments earn their keep here, as the next script shows.
//@version=6
indicator("Grouped inputs", overlay = true)
GRP_MA = "Moving average"
GRP_STYLE = "Style"
maType = input.string("EMA", "Type", options = ["SMA", "EMA", "WMA"], group = GRP_MA)
maLen = input.int(50, "Length", minval = 1, group = GRP_MA)
upColor = input.color(color.green, "Up", inline = "clr", group = GRP_STYLE)
downColor = input.color(color.red, "Down", inline = "clr", group = GRP_STYLE)
lineWidth = input.int(2, "Width", minval = 1, maxval = 4, group = GRP_STYLE)
smaValue = ta.sma(close, maLen)
emaValue = ta.ema(close, maLen)
wmaValue = ta.wma(close, maLen)
ma = switch maType
"SMA" => smaValue
"WMA" => wmaValue
=> emaValue
plot(ma, "MA", color = ma >= ma[1] ? upColor : downColor, linewidth = lineWidth)Two details in this script are deliberate. The group names are stored in constants so a typo in one input cannot silently create a third group. And all three averages are calculated on every bar before the switch picks one, because functions such as ta.ema depend on their previous values and should not be called inside a branch that only runs some of the time. If you want the colors to react to strength rather than direction, the color gradient guide shows how to compute them instead of asking for two inputs.
Grouping and layout: group, inline, and tooltip
Layout is where inputs stop being a coding problem and become a design problem. A settings dialog is a small user interface, and a script with ten settings and no structure reads as unfinished.
The group argument places inputs under a shared heading. Inputs with the same group string are gathered together, in the order they appear in your code. The inline argument puts inputs that share an identifier on one row, which suits a pair like an up color and a down color, or a length and its on/off checkbox. Keep inline rows to two or three items so each field stays readable in the narrow dialog.
The tooltip argument is the right home for explanations. A short title such as "Length" is easier to scan than "Length of the moving average in bars", and the longer sentence still reaches the user who hovers over the question mark.
As a rough rule, once a script has more than five or six inputs, give every one of them a group. Order the groups by how often people change them: the core calculation first, the styling after, and the rarely touched options last.
Time-aware inputs: input.timeframe, input.session, input.symbol, input.time
Four more types handle chart context instead of plain values.
input.timeframe shows a timeframe picker and returns a string such as "240" or "D". It is the natural partner of request.security, since a higher-timeframe indicator is only useful if the user can choose the timeframe. input.session returns a session string such as "0800-1600" and gives the user a proper session picker, which you test against the current bar with time(). input.symbol shows a symbol search box and returns the ticker as a string, which you can pass to request.security to compare the chart against another market. input.time returns a timestamp and is used for choosing a start date.
//@version=6
indicator("Timeframe and session inputs", overlay = true)
htf = input.timeframe("240", "Higher timeframe")
sess = input.session("0800-1600", "Session")
htfClose = request.security(syminfo.tickerid, htf, close[1], lookahead = barmerge.lookahead_on)
inSession = not na(time(timeframe.period, sess))
bgcolor(inSession ? color.new(color.blue, 90) : na)
plot(htfClose, "Previous higher timeframe close", color = color.orange)The higher timeframe close is read one bar back with lookahead turned on, which is the pattern that avoids showing a value the script could not have known on the live bar. The reasoning behind that pattern is in our posts on how request.security works and on the lookahead setting. The session check is covered in more depth in the session and time filter guide, including the bugs that appear around midnight.
A choice of timeframe that is lower than the chart's timeframe usually returns misleading results, and no input argument will prevent it. If it matters, check timeframe.in_seconds(htf) >= timeframe.in_seconds() in your code and raise a runtime.error with a clear message.
Prices, text areas, and confirm: input.price and input.text_area
input.price is an unusual one. It behaves like input.float, but it adds a way to click on the chart to pick the value. Combine it with confirm = true and the script asks the user to click a price as soon as it is added, which is a pleasant way to set a level, such as a line to watch or a zone boundary.
//@version=6
indicator("Pick a level on the chart", overlay = true)
level = input.price(100.0, "Level", confirm = true, tooltip = "Click the chart to choose a price")
hline(level, "Level", color = color.orange)input.text_area returns a multi-line string, and is useful for pasting a list of values, symbols, or notes that the script then splits with str.split. It takes a default and a title and little else. Use it sparingly: free text is the hardest input to validate, and a script that depends on a pasted list needs a plan for what happens when the format is wrong.
input.enum: a dropdown that cannot contain a typo
The options argument on input.string has a weakness. The strings in your list and the strings you compare against in your code are separate pieces of text, and nothing checks that they match. Misspell one and the branch silently never runs.
Pine Script v6 adds input.enum to solve this. You declare an enum with named members, pass one as the default, and the dropdown shows each member's title. Comparisons use the enum itself, so a typo becomes a compile error instead of a quiet bug. Our enums guide covers the declaration syntax in detail; here is the input side.
//@version=6
indicator("Enum input", overlay = true)
enum MaKind
sma = "SMA"
ema = "EMA"
wma = "WMA"
kind = input.enum(MaKind.ema, "Average type")
len = input.int(34, "Length", minval = 1)
smaValue = ta.sma(close, len)
emaValue = ta.ema(close, len)
wmaValue = ta.wma(close, len)
ma = switch kind
MaKind.sma => smaValue
MaKind.ema => emaValue
MaKind.wma => wmaValue
plot(ma, "Average")For any dropdown you build from scratch in v6, input.enum is the better choice. Keep input.string with options for cases where the list really is just text, or where you are maintaining an older script.
Common mistakes and how to read the error
A few errors account for most of the pain with inputs. If you see a message that mentions a mismatched input, the cause is usually plain syntax rather than an input.* call, and the mismatched input error guide helps you read it.
A default that is outside its own limits fails. input.int(0, "Length", minval = 1) is rejected because zero is below the minimum.
A default that is not in options fails, as covered above.
Check the reference before combining minval or maxval with options: a dropdown already has fixed values, so the numeric limits add nothing.
Passing a series value as a default fails. The default of an input must be a constant, so input.float(close, "Level") is invalid. Use input.source if you want the user to choose a series, or input.price with a fixed number if you want a level.
Finally, inputs read once per run. If you want a setting that changes during the script, such as a counter, that is a variable, not an input.
Putting it into practice
When you design the inputs for a new script, work from the user's point of view. Start by listing the decisions a person might reasonably want to change, then drop anything they would never touch. Pick the narrowest type for each, so a length is an input.int with a minval, and a mode is an input.enum. Give every input a title and a group, and move explanations into tooltips. Last, open the dialog yourself and read it from top to bottom like a stranger would.
This is also a place where an AI editor saves real time, because the work is repetitive and the rules are exact. In PineScripter, you can describe the settings you want in plain English, for example a length with a minimum of one, a choice between three average types, and up and down colors on one row, and the AI code editor writes the Pine Script v6 for them. It checks the script for compile errors the way TradingView does, fixes what it finds, and when you ask for a change afterward it edits only the affected lines. You can then run the script on the chart inside PineScripter to see what it plots before pasting it into TradingView.
PineScripter is a coding tool. It writes and checks Pine Script, and it does not give trading advice or make any claim about results.
Disclaimer: this article is educational and describes how to write Pine Script. It is not financial advice, and nothing here predicts or promises any trading outcome.
Frequently asked questions
What is the difference between input.int and input.float in Pine Script?
input.int returns a whole number and suits lengths and bar counts. input.float returns a decimal and suits multipliers, thresholds, and percentages. Both accept minval, maxval, and step.
How do I make a dropdown input in Pine Script?
Add the options argument to input.string, with the default set to one of the listed values. In v6 you can also use input.enum, which checks the choices at compile time so a misspelled option becomes an error instead of a silent bug.
How do I put several inputs on one row?
Give the inputs the same inline identifier string. They then share a row in the Settings dialog. Two or three items per row is a practical limit.
Can an input default be a series like close?
No. The default must be a constant. To let the user choose a series, use input.source. To let them choose a price level, use input.price with a fixed number.
What does confirm = true do?
It asks the user to confirm the input value when the script is first added to the chart. With input.price, the user can click the chart to pick the price.
Describe your indicator in plain English and get Pine Script that compiles →
Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.