One octet, 0 to 255
A regex has no notion of numbers, so the range is spelled out digit by digit:
25[0-5] 250–255
2[0-4]\d 200–249
1\d\d 100–199
[1-9]?\d 0–99, without a leading zero
Three octets with a dot, (?:…\.){3}, then a fourth without, and \b on both ends so that 1192.168.0.1 is not partly matched. To validate a whole input field instead of finding addresses in text, replace the \bs with ^ and $.
Often simpler: no regex at all
// Node.js
require('node:net').isIPv4('192.168.0.1') // true
# Python
import ipaddress; ipaddress.IPv4Address('192.168.0.1')
// PHP
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
// C#
System.Net.IPAddress.TryParse(s, out var ip) // also accepts short forms like "1.2" Questions
Why not just \d{1,3}(\.\d{1,3}){3}?
It also accepts 999.999.999.999. That is fine for a rough search in logs, but not for validation.
Are leading zeros like 192.168.01.1 valid?
Many parsers read them as octal (01 = 1, but 010 = 8), which has caused security bugs. This pattern rejects them on purpose.
What about IPv6?
IPv6 has many short forms (::1, ::ffff:1.2.3.4); a regex for it is long and fragile. Use your language's IP parser instead.